};
struct pakfire_jail_exec {
+ struct pakfire_jail* jail;
+
sd_event* loop;
int flags;
+ // Exit Code
+ int exit;
+
// PID (of the child)
pid_t pid;
int pidfd;
// Socket to pass FDs
int socket[2];
- // Process status (from waitid)
- siginfo_t status;
-
// FD to notify the client that the parent has finished initialization
int completed_fd;
char garbage[8];
int r = 0;
- // Fetch file descriptors from context
- const int pidfd = ctx->pidfd;
-
// Fetch the UNIX domain socket
const int socket_recv = pakfire_jail_get_pipe_to_read(jail, &ctx->socket);
// Timer
{ timerfd, EPOLLIN },
- // Child Process
- { ctx->pidfd, EPOLLIN },
-
// UNIX Domain Socket
{ socket_recv, EPOLLIN },
goto ERROR;
}
- // Handle any changes to the PIDFD
- } else if (pidfd == fd) {
- if (e & EPOLLIN) {
- // Call waidid() and store the result
- r = waitid(P_PIDFD, ctx->pidfd, &ctx->status, WEXITED);
- if (r) {
- CTX_ERROR(jail->ctx, "waitid() failed: %m\n");
- goto ERROR;
- }
-
- // Mark that we have ended so that we will process the remaining
- // events from epoll() now, but won't restart the outer loop.
- ended = 1;
- }
-
// Handle timer events
} else if (timerfd == fd) {
if (e & EPOLLIN) {
CTX_DEBUG(jail->ctx, "Terminating process...\n");
// Send SIGTERM to the process
- r = pidfd_send_signal(pidfd, SIGKILL, NULL, 0);
+ r = pidfd_send_signal(ctx->pidfd, SIGKILL, NULL, 0);
if (r) {
CTX_ERROR(jail->ctx, "Could not kill process: %m\n");
goto ERROR;
return r;
}
+static int pakfire_jail_exited(sd_event_source* source, const siginfo_t* si, void* data) {
+ struct pakfire_jail_exec* ctx = data;
+ struct pakfire_jail* jail = ctx->jail;
+
+ switch (si->si_code) {
+ case CLD_EXITED:
+ CTX_DEBUG(jail->ctx, "Process has exited with status code %d\n", si->si_status);
+
+ // Store the exit code
+ ctx->exit = si->si_status;
+ break;
+
+ case CLD_KILLED:
+ CTX_ERROR(jail->ctx, "Process has been killed by signal %d\n", si->si_signo);
+
+ // Store the exit code
+ ctx->exit = 139;
+ break;
+
+ case CLD_DUMPED:
+ CTX_ERROR(jail->ctx, "The child process terminated abnormally\n");
+ break;
+ }
+
+ // Terminate the event loop
+ return sd_event_exit(sd_event_source_get_event(source), 0);
+}
+
/*
Performs the initialisation that needs to happen in the parent part
*/
static int pakfire_jail_parent(struct pakfire_jail* jail, struct pakfire_jail_exec* ctx) {
int r;
+ // Register the PID file descriptor
+ r = sd_event_add_child_pidfd(ctx->loop, NULL, ctx->pidfd, WEXITED, pakfire_jail_exited, ctx);
+ if (r < 0) {
+ CTX_DEBUG(jail->ctx, "Could not register the child process with the event loop: %s\n", strerror(-r));
+ return r;
+ }
+
// Setup UID mapping
r = pakfire_jail_setup_uid_mapping(jail, ctx->pid);
if (r)
// Run a command in the jail
PAKFIRE_EXPORT int pakfire_jail_exec(struct pakfire_jail* jail, const char* argv[], int flags) {
- int exit = -1;
int r;
// Check if argv is valid
// Initialize context for this call
struct pakfire_jail_exec ctx = {
+ .jail = jail,
.flags = flags,
+ // Exit Code
+ .exit = -1,
+
.socket = { -1, -1 },
.pidfd = -1,
if (r)
goto ERROR;
- // Handle exit status
- switch (ctx.status.si_code) {
- case CLD_EXITED:
- CTX_DEBUG(jail->ctx, "The child process exited with code %d\n",
- ctx.status.si_status);
-
- // Pass exit code
- exit = ctx.status.si_status;
- break;
-
- case CLD_KILLED:
- CTX_ERROR(jail->ctx, "The child process was killed\n");
- exit = 139;
- break;
-
- case CLD_DUMPED:
- CTX_ERROR(jail->ctx, "The child process terminated abnormally\n");
- break;
-
- // Log anything else
- default:
- CTX_ERROR(jail->ctx, "Unknown child exit code: %d\n", ctx.status.si_code);
- break;
- }
-
ERROR:
// Reset all callbacks
pakfire_jail_set_stdin_callback(jail, NULL, NULL);
if (ctx.loop)
sd_event_unref(ctx.loop);
- return exit;
+ return ctx.exit;
}
static int pakfire_jail_exec_interactive(