#include <sys/personality.h>
#include <sys/prctl.h>
#include <sys/resource.h>
+#include <sys/timerfd.h>
#include <sys/types.h>
#include <sys/wait.h>
// Resource Limits
int nice;
+ // Timeout
+ struct itimerspec timeout;
+
// CGroup
struct pakfire_cgroup* cgroup;
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;
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.
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
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);
// 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
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;
ERROR:
if (epollfd > 0)
close(epollfd);
+ if (timerfd > 0)
+ close(timerfd);
return r;
}
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);
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);
}