+ if (*fd_read >= 0)
+ return *fd_read;
+
+ return -1;
+}
+
+static int pakfire_jail_get_pipe_to_write(struct pakfire_jail* jail, int (*fds)[2]) {
+ // Give the variables easier names to avoid confusion
+ int* fd_read = &(*fds)[0];
+ int* fd_write = &(*fds)[1];
+
+ // Close the read end of the pipe
+ if (*fd_read >= 0) {
+ close(*fd_read);
+ *fd_read = -1;
+ }
+
+ // Return the write end
+ if (*fd_write >= 0)
+ return *fd_write;
+
+ return -1;
+}
+
+static int pakfire_jail_recv_fd(struct pakfire_jail* jail, int socket, int* fd) {
+ const size_t payload_length = sizeof(fd);
+ char buffer[CMSG_SPACE(payload_length)];
+ int r;
+
+ struct msghdr msg = {
+ .msg_control = buffer,
+ .msg_controllen = sizeof(buffer),
+ };
+
+ // Receive the message
+ r = recvmsg(socket, &msg, 0);
+ if (r) {
+ CTX_ERROR(jail->ctx, "Could not receive file descriptor: %s\n", strerror(errno));
+ return -errno;
+ }
+
+ // Fetch the payload
+ struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+ if (!cmsg)
+ return -EBADMSG;
+
+ *fd = *((int*)CMSG_DATA(cmsg));
+
+ CTX_DEBUG(jail->ctx, "Received fd %d from socket %d\n", *fd, socket);
+
+ return 0;
+}
+
+static int pakfire_jail_send_fd(struct pakfire_jail* jail, int socket, int fd) {
+ const size_t payload_length = sizeof(fd);
+ char buffer[CMSG_SPACE(payload_length)];
+ int r;
+
+ CTX_DEBUG(jail->ctx, "Sending fd %d to socket %d\n", fd, socket);
+
+ // Header
+ struct msghdr msg = {
+ .msg_control = buffer,
+ .msg_controllen = sizeof(buffer),
+ };
+
+ // Payload
+ struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(payload_length);
+
+ // Set payload
+ *((int*)CMSG_DATA(cmsg)) = fd;
+
+ // Send the message
+ r = sendmsg(socket, &msg, 0);
+ if (r) {
+ CTX_ERROR(jail->ctx, "Could not send file descriptor: %s\n", strerror(errno));
+ return -errno;
+ }
+
+ return 0;
+}
+
+static int pakfire_jail_log(struct pakfire* pakfire, void* data, int priority,
+ const char* line, const size_t length) {
+ // Pass everything to the parent logger
+ pakfire_log_condition(pakfire, priority, 0, "%.*s", (int)length, line);
+
+ return 0;
+}
+
+static int pakfire_jail_epoll_add_fd(struct pakfire_jail* jail, int epollfd, int fd, int events) {
+ struct epoll_event event = {
+ .events = events|EPOLLHUP,
+ .data = {
+ .fd = fd,
+ },
+ };
+ int r;
+
+ // Read flags
+ int flags = fcntl(fd, F_GETFL, 0);
+
+ // Set modified flags
+ r = fcntl(fd, F_SETFL, flags|O_NONBLOCK);
+ if (r < 0) {
+ CTX_ERROR(jail->ctx, "Could not set file descriptor %d into non-blocking mode: %s\n",
+ fd, strerror(errno));
+ return -errno;
+ }
+
+ // Add the file descriptor to the loop
+ r = epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event);
+ if (r < 0) {
+ ERROR(jail->pakfire, "Could not add file descriptor %d to epoll(): %s\n",
+ fd, strerror(errno));
+ return -errno;
+ }
+
+ return 0;
+}
+
+static int pakfire_jail_setup_child2(struct pakfire_jail* jail, struct pakfire_jail_exec* ctx);
+
+static int pakfire_jail_wait_on_child(struct pakfire_jail* jail, int pidfd) {
+ siginfo_t status = {};
+ int r;
+
+ // Call waitid() and store the result
+ r = waitid(P_PIDFD, pidfd, &status, WEXITED);
+ if (r) {
+ CTX_ERROR(jail->ctx, "waitid() failed: %s\n", strerror(errno));
+ return -errno;
+ }
+
+ switch (status.si_code) {
+ // If the process exited normally, we return the exit code
+ case CLD_EXITED:
+ CTX_DEBUG(jail->ctx, "The child process exited with code %d\n", status.si_status);
+ return status.si_status;
+
+ case CLD_KILLED:
+ CTX_ERROR(jail->ctx, "The child process was killed\n");
+ return 139;
+
+ case CLD_DUMPED:
+ CTX_ERROR(jail->ctx, "The child process terminated abnormally\n");
+ return 139;
+
+ // Log anything else
+ default:
+ CTX_ERROR(jail->ctx, "Unknown child exit code: %d\n", status.si_code);
+ break;
+ }
+
+ return -EBADMSG;