]> git.ipfire.org Git - people/ms/pakfire.git/blobdiff - src/libpakfire/jail.c
jail: Bring back helper function to add FDs to epoll()
[people/ms/pakfire.git] / src / libpakfire / jail.c
index 52291408dc2c7fb8663698f4c100aefb94d847b8..5cc1dd753d366f1e62688e3705ad74073f375389 100644 (file)
@@ -35,7 +35,6 @@
 #include <sys/personality.h>
 #include <sys/prctl.h>
 #include <sys/resource.h>
-#include <sys/signalfd.h>
 #include <sys/timerfd.h>
 #include <sys/types.h>
 #include <sys/wait.h>
@@ -89,6 +88,7 @@ struct pakfire_jail_mountpoint {
 };
 
 struct pakfire_jail {
+       struct pakfire_ctx* ctx;
        struct pakfire* pakfire;
        int nrefs;
 
@@ -147,7 +147,9 @@ struct pakfire_jail_exec {
                // Logging
                int log_INFO[2];
                int log_ERROR[2];
+#ifdef ENABLE_DEBUG
                int log_DEBUG[2];
+#endif /* ENABLE_DEBUG */
        } pipes;
 
        // Communicate
@@ -165,11 +167,17 @@ struct pakfire_jail_exec {
                // Logging
                struct pakfire_log_buffer log_INFO;
                struct pakfire_log_buffer log_ERROR;
+#ifdef ENABLE_DEBUG
                struct pakfire_log_buffer log_DEBUG;
+#endif /* ENABLE_DEBUG */
        } buffers;
 
        struct pakfire_cgroup* cgroup;
        struct pakfire_cgroup_stats cgroup_stats;
+
+       // Console
+       char console[PATH_MAX];
+       int consolefd;
 };
 
 static int clone3(struct clone_args* args, size_t size) {
@@ -198,8 +206,10 @@ static void pakfire_jail_free(struct pakfire_jail* jail) {
 
        if (jail->cgroup)
                pakfire_cgroup_unref(jail->cgroup);
-
-       pakfire_unref(jail->pakfire);
+       if (jail->pakfire)
+               pakfire_unref(jail->pakfire);
+       if (jail->ctx)
+               pakfire_ctx_unref(jail->ctx);
        free(jail);
 }
 
@@ -269,6 +279,9 @@ PAKFIRE_EXPORT int pakfire_jail_create(struct pakfire_jail** jail, struct pakfir
        if (!j)
                return 1;
 
+       // Reference context
+       j->ctx = pakfire_ctx(pakfire);
+
        // Reference Pakfire
        j->pakfire = pakfire_ref(pakfire);
 
@@ -513,38 +526,12 @@ static int pakfire_jail_create_timer(struct pakfire_jail* jail) {
        return fd;
 
 ERROR:
-       if (fd > 0)
+       if (fd >= 0)
                close(fd);
 
        return -1;
 }
 
-// Signals
-
-static int pakfire_jail_handle_signals(struct pakfire_jail* jail) {
-       sigset_t mask;
-       int r;
-
-       sigemptyset(&mask);
-       sigaddset(&mask, SIGINT);
-
-       // Block signals
-       r = sigprocmask(SIG_BLOCK, &mask, NULL);
-       if (r < 0) {
-               ERROR(jail->pakfire, "Failed to block signals: %m\n");
-               return r;
-       }
-
-       // Create a file descriptor
-       r = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
-       if (r < 0) {
-               ERROR(jail->pakfire, "Failed to create signalfd: %m\n");
-               return r;
-       }
-
-       return r;
-}
-
 /*
        This function replaces any logging in the child process.
 
@@ -576,7 +563,7 @@ static void pakfire_jail_log_redirect(void* data, int priority, const char* file
        }
 
        // Send the log message
-       if (fd)
+       if (fd >= 0)
                vdprintf(fd, format, args);
 }
 
@@ -683,7 +670,7 @@ static int pakfire_jail_stream_stdin(struct pakfire_jail* jail,
                close(fd);
 
                // Reset the file-descriptor so it won't be closed again later
-               ctx->pipes.stdin[1] = 0;
+               ctx->pipes.stdin[1] = -1;
 
                // Report success
                r = 0;
@@ -704,7 +691,7 @@ static int pakfire_jail_setup_pipe(struct pakfire_jail* jail, int (*fds)[2], con
 
 static void pakfire_jail_close_pipe(struct pakfire_jail* jail, int fds[2]) {
        for (unsigned int i = 0; i < 2; i++)
-               if (fds[i])
+               if (fds[i] >= 0)
                        close(fds[i]);
 }
 
@@ -718,13 +705,16 @@ static int pakfire_jail_get_pipe_to_read(struct pakfire_jail* jail, int (*fds)[2
        int* fd_write = &(*fds)[1];
 
        // Close the write end of the pipe
-       if (*fd_write) {
+       if (*fd_write >= 0) {
                close(*fd_write);
                *fd_write = -1;
        }
 
        // Return the read end
-       return *fd_read;
+       if (*fd_read >= 0)
+               return *fd_read;
+
+       return -1;
 }
 
 static int pakfire_jail_get_pipe_to_write(struct pakfire_jail* jail, int (*fds)[2]) {
@@ -733,13 +723,16 @@ static int pakfire_jail_get_pipe_to_write(struct pakfire_jail* jail, int (*fds)[
        int* fd_write = &(*fds)[1];
 
        // Close the read end of the pipe
-       if (*fd_read) {
+       if (*fd_read >= 0) {
                close(*fd_read);
                *fd_read = -1;
        }
 
        // Return the write end
-       return *fd_write;
+       if (*fd_write >= 0)
+               return *fd_write;
+
+       return -1;
 }
 
 static int pakfire_jail_log(struct pakfire* pakfire, void* data, int priority,
@@ -750,11 +743,40 @@ static int pakfire_jail_log(struct pakfire* pakfire, void* data, int priority,
        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_wait(struct pakfire_jail* jail, struct pakfire_jail_exec* ctx) {
        int epollfd = -1;
-       struct epoll_event ev;
        struct epoll_event events[EPOLL_MAX_EVENTS];
-       struct signalfd_siginfo siginfo;
        char garbage[8];
        int r = 0;
 
@@ -770,14 +792,35 @@ static int pakfire_jail_wait(struct pakfire_jail* jail, struct pakfire_jail_exec
        // 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);
+#ifdef ENABLE_DEBUG
        const int log_DEBUG = pakfire_jail_get_pipe_to_read(jail, &ctx->pipes.log_DEBUG);
-
-       // Signals
-       const int signalfd = pakfire_jail_handle_signals(jail);
+#endif /* ENABLE_DEBUG */
 
        // Make a list of all file descriptors we are interested in
-       const int fds[] = {
-               stdin, stdout, stderr, pidfd, timerfd, signalfd, log_INFO, log_ERROR, log_DEBUG,
+       const struct pakfire_wait_fds {
+               const int fd;
+               const int events;
+       } fds[] = {
+               // Standard input/output
+               { stdin,  EPOLLOUT },
+               { stdout, EPOLLIN },
+               { stderr, EPOLLIN },
+
+               // Timer
+               { timerfd, EPOLLIN },
+
+               // Child Process
+               { ctx->pidfd, EPOLLIN },
+
+               // Log Pipes
+               { log_INFO, EPOLLIN },
+               { log_ERROR, EPOLLIN },
+#ifdef ENABLE_DEBUG
+               { log_DEBUG, EPOLLIN },
+#endif /* ENABLE_DEBUG */
+
+               // Sentinel
+               { -1, 0 },
        };
 
        // Setup epoll
@@ -789,38 +832,15 @@ static int pakfire_jail_wait(struct pakfire_jail* jail, struct pakfire_jail_exec
        }
 
        // Turn file descriptors into non-blocking mode and add them to epoll()
-       for (unsigned int i = 0; i < sizeof(fds) / sizeof(*fds); i++) {
-               int fd = fds[i];
-
+       for (const struct pakfire_wait_fds* fd = fds; fd->events; fd++) {
                // Skip fds which were not initialized
-               if (fd < 0)
+               if (fd->fd < 0)
                        continue;
 
-               ev.events = EPOLLHUP;
-
-               if (fd == stdin)
-                       ev.events |= EPOLLOUT;
-               else
-                       ev.events |= EPOLLIN;
-
-               // Read flags
-               int flags = fcntl(fd, F_GETFL, 0);
-
-               // Set modified flags
-               if (fcntl(fd, F_SETFL, flags|O_NONBLOCK) < 0) {
-                       ERROR(jail->pakfire,
-                               "Could not set file descriptor %d into non-blocking mode: %m\n", fd);
-                       r = 1;
-                       goto ERROR;
-               }
-
-               ev.data.fd = fd;
-
-               if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) < 0) {
-                       ERROR(jail->pakfire, "Could not add file descriptor %d to epoll(): %m\n", fd);
-                       r = 1;
+               // Add the FD to the event loop
+               r = pakfire_jail_epoll_add_fd(jail, epollfd, fd->fd, fd->events);
+               if (r)
                        goto ERROR;
-               }
        }
 
        int ended = 0;
@@ -888,37 +908,6 @@ static int pakfire_jail_wait(struct pakfire_jail* jail, struct pakfire_jail_exec
                                                }
                                        }
 
-                                       // There is nothing else to do
-                                       continue;
-
-                               // Handle signals
-                               } else if (fd == signalfd) {
-                                       // Read the signal
-                                       r = read(signalfd, &siginfo, sizeof(siginfo));
-                                       if (r < 1) {
-                                               ERROR(jail->pakfire, "Could not read signal: %m\n");
-                                               goto ERROR;
-                                       }
-
-                                       DEBUG(jail->pakfire, "Received signal %u\n", siginfo.ssi_signo);
-
-                                       // Handle signals
-                                       switch (siginfo.ssi_signo) {
-                                               // Pass SIGINT down to the child process
-                                               case SIGINT:
-                                                       r = pidfd_send_signal(pidfd, siginfo.ssi_signo, NULL, 0);
-                                                       if (r) {
-                                                               ERROR(jail->pakfire, "Could not send signal to process: %m\n");
-                                                               goto ERROR;
-                                                       }
-                                                       break;
-
-                                               default:
-                                                       ERROR(jail->pakfire, "Received unhandled signal %u\n",
-                                                               siginfo.ssi_signo);
-                                                       break;
-                                       }
-
                                        // Don't fall through to log processing
                                        continue;
 
@@ -935,11 +924,13 @@ static int pakfire_jail_wait(struct pakfire_jail* jail, struct pakfire_jail_exec
 
                                        callback = pakfire_jail_log;
 
+#ifdef ENABLE_DEBUG
                                } else if (fd == log_DEBUG) {
                                        buffer = &ctx->buffers.log_DEBUG;
                                        priority = LOG_DEBUG;
 
                                        callback = pakfire_jail_log;
+#endif /* ENABLE_DEBUG */
 
                                // Handle anything from the log pipes
                                } else if (fd == stdout) {
@@ -1010,12 +1001,10 @@ static int pakfire_jail_wait(struct pakfire_jail* jail, struct pakfire_jail_exec
        }
 
 ERROR:
-       if (epollfd > 0)
+       if (epollfd >= 0)
                close(epollfd);
-       if (timerfd > 0)
+       if (timerfd >= 0)
                close(timerfd);
-       if (signalfd > 0)
-               close(signalfd);
 
        return r;
 }
@@ -1320,7 +1309,17 @@ static int pakfire_jail_mount(struct pakfire_jail* jail, struct pakfire_jail_exe
                flags |= PAKFIRE_MOUNT_LOOP_DEVICES;
 
        // Mount all default stuff
-       r = pakfire_mount_all(jail->pakfire, flags);
+       r = pakfire_mount_all(jail->pakfire, PAKFIRE_MNTNS_OUTER, flags);
+       if (r)
+               return r;
+
+       // Populate /dev
+       r = pakfire_populate_dev(jail->pakfire, flags);
+       if (r)
+               return r;
+
+       // Mount the interpreter (if needed)
+       r = pakfire_mount_interpreter(jail->pakfire);
        if (r)
                return r;
 
@@ -1342,9 +1341,6 @@ static int pakfire_jail_mount(struct pakfire_jail* jail, struct pakfire_jail_exe
                        return r;
        }
 
-       // Log all mountpoints
-       pakfire_mount_list(jail->pakfire);
-
        return 0;
 }
 
@@ -1490,7 +1486,7 @@ static int pakfire_jail_setup_gid_mapping(struct pakfire_jail* jail, pid_t pid)
                        "0 %lu %lu\n", subgid->id, subgid->length);
        } else {
                r = pakfire_file_write(jail->pakfire, path, 0, 0, 0,
-                       "0 %lu 1\n%1 %lu %lu\n", gid, subgid->id, subgid->length);
+                       "0 %lu 1\n1 %lu %lu\n", gid, subgid->id, subgid->length);
        }
 
        if (r) {
@@ -1503,38 +1499,19 @@ static int pakfire_jail_setup_gid_mapping(struct pakfire_jail* jail, pid_t pid)
 
 static int pakfire_jail_setgroups(struct pakfire_jail* jail, pid_t pid) {
        char path[PATH_MAX];
-       int r = 1;
+       int r;
 
        // Make path
        r = pakfire_string_format(path, "/proc/%d/setgroups", pid);
        if (r)
                return r;
 
-       // Open file for writing
-       FILE* f = fopen(path, "w");
-       if (!f) {
-               ERROR(jail->pakfire, "Could not open %s for writing: %m\n", path);
-               goto ERROR;
-       }
-
-       // Write content
-       int bytes_written = fprintf(f, "deny\n");
-       if (bytes_written <= 0) {
-               ERROR(jail->pakfire, "Could not write to %s: %m\n", path);
-               goto ERROR;
-       }
-
-       r = fclose(f);
-       f = NULL;
+       r = pakfire_file_write(jail->pakfire, path, 0, 0, 0, "deny\n");
        if (r) {
-               ERROR(jail->pakfire, "Could not close %s: %m\n", path);
-               goto ERROR;
+               CTX_ERROR(jail->ctx, "Could not set setgroups to deny: %s\n", strerror(errno));
+               r = -errno;
        }
 
-ERROR:
-       if (f)
-               fclose(f);
-
        return r;
 }
 
@@ -1545,10 +1522,10 @@ static int pakfire_jail_send_signal(struct pakfire_jail* jail, int fd) {
        DEBUG(jail->pakfire, "Sending signal...\n");
 
        // Write to the file descriptor
-       ssize_t bytes_written = write(fd, &val, sizeof(val));
-       if (bytes_written < 0 || (size_t)bytes_written < sizeof(val)) {
-               ERROR(jail->pakfire, "Could not send signal: %m\n");
-               r = 1;
+       r = eventfd_write(fd, val);
+       if (r < 0) {
+               ERROR(jail->pakfire, "Could not send signal: %s\n", strerror(errno));
+               r = -errno;
        }
 
        // Close the file descriptor
@@ -1563,10 +1540,10 @@ static int pakfire_jail_wait_for_signal(struct pakfire_jail* jail, int fd) {
 
        DEBUG(jail->pakfire, "Waiting for signal...\n");
 
-       ssize_t bytes_read = read(fd, &val, sizeof(val));
-       if (bytes_read < 0 || (size_t)bytes_read < sizeof(val)) {
-               ERROR(jail->pakfire, "Error waiting for signal: %m\n");
-               r = 1;
+       r = eventfd_read(fd, &val);
+       if (r < 0) {
+               ERROR(jail->pakfire, "Error waiting for signal: %s\n", strerror(errno));
+               r = -errno;
        }
 
        // Close the file descriptor
@@ -1634,12 +1611,37 @@ static int pakfire_jail_switch_root(struct pakfire_jail* jail, const char* root)
        return 0;
 }
 
+#if 0
+static int pakfire_jail_open_pty(struct pakfire_jail* jail, struct pakfire_jail_exec* ctx) {
+       int r;
+
+       // Allocate a new PTY
+       ctx->consolefd = posix_openpt(O_RDWR|O_NONBLOCK|O_NOCTTY|O_CLOEXEC);
+       if (ctx->consolefd < 0)
+               return -errno;
+
+       // Fetch the path
+       r = ptsname_r(ctx->consolefd, ctx->console, sizeof(ctx->console));
+       if (r)
+               return -r;
+
+       CTX_DEBUG(jail->ctx, "Allocated console at %s (%d)\n", ctx->console, ctx->consolefd);
+
+       // Create a symlink
+       r = pakfire_symlink(jail->ctx, "/dev/console", ctx->console);
+       if (r)
+               return r;
+
+       return r;
+}
+#endif
+
 static int pakfire_jail_child(struct pakfire_jail* jail, struct pakfire_jail_exec* ctx,
                const char* argv[]) {
        int r;
 
        // Redirect any logging to our log pipe
-       pakfire_set_log_callback(jail->pakfire, pakfire_jail_log_redirect, &ctx->pipes);
+       pakfire_ctx_set_log_callback(jail->ctx, pakfire_jail_log_redirect, &ctx->pipes);
 
        // Fetch my own PID
        pid_t pid = getpid();
@@ -1683,17 +1685,47 @@ static int pakfire_jail_child(struct pakfire_jail* jail, struct pakfire_jail_exe
        DEBUG(jail->pakfire, "  UID: %u (effective %u)\n", uid, euid);
        DEBUG(jail->pakfire, "  GID: %u (effective %u)\n", gid, egid);
 
-       // Check if we are (effectively running as root)
+       // Log all mountpoints
+       pakfire_mount_list(jail->ctx);
+
+       // Fail if we are not PID 1
+       if (pid != 1) {
+               CTX_ERROR(jail->ctx, "Child process is not PID 1\n");
+               return 126;
+       }
+
+       // Fail if we are not running as root
        if (uid || gid || euid || egid) {
                ERROR(jail->pakfire, "Child process is not running as root\n");
                return 126;
        }
 
+       // Mount all default stuff
+       r = pakfire_mount_all(jail->pakfire, PAKFIRE_MNTNS_INNER, 0);
+       if (r)
+               return 126;
+
+#if 0
+       // Create a new session
+       r = setsid();
+       if (r < 0) {
+               CTX_ERROR(jail->ctx, "Could not create a new session: %s\n", strerror(errno));
+               return 126;
+       }
+
+       // Allocate a new PTY
+       r = pakfire_jail_open_pty(jail, ctx);
+       if (r) {
+               CTX_ERROR(jail->ctx, "Could not allocate a new PTY: %s\n", strerror(-r));
+               return 126;
+       }
+#endif
+
        const char* root = pakfire_get_path(jail->pakfire);
        const char* arch = pakfire_get_effective_arch(jail->pakfire);
 
        // Change mount propagation to slave to receive anything from the parent namespace
-       r = pakfire_mount_change_propagation(jail->pakfire, MS_SLAVE, "/");
+       r = pakfire_mount_change_propagation(jail->ctx, "/", MS_SLAVE);
        if (r)
                return r;
 
@@ -1703,7 +1735,7 @@ static int pakfire_jail_child(struct pakfire_jail* jail, struct pakfire_jail_exe
                return r;
 
        // Change mount propagation to private
-       r = pakfire_mount_change_propagation(jail->pakfire, MS_PRIVATE, root);
+       r = pakfire_mount_change_propagation(jail->ctx, root, MS_PRIVATE);
        if (r)
                return r;
 
@@ -1862,9 +1894,14 @@ static int __pakfire_jail_exec(struct pakfire_jail* jail, const char* argv[],
                .flags = flags,
 
                .pipes = {
-                       .stdin  = { -1, -1 },
-                       .stdout = { -1, -1 },
-                       .stderr = { -1, -1 },
+                       .stdin     = { -1, -1 },
+                       .stdout    = { -1, -1 },
+                       .stderr    = { -1, -1 },
+                       .log_INFO  = { -1, -1 },
+                       .log_ERROR = { -1, -1 },
+#ifdef ENABLE_DEBUG
+                       .log_DEBUG = { -1, -1 },
+#endif /* ENABLE_DEBUG */
                },
 
                .communicate = {
@@ -2021,13 +2058,8 @@ ERROR:
        // Destroy the temporary cgroup (if any)
        if (ctx.cgroup) {
                // Read cgroup stats
-               r = pakfire_cgroup_stat(ctx.cgroup, &ctx.cgroup_stats);
-               if (r) {
-                       ERROR(jail->pakfire, "Could not read cgroup stats: %m\n");
-               } else {
-                       pakfire_cgroup_stat_dump(ctx.cgroup, &ctx.cgroup_stats);
-               }
-
+               pakfire_cgroup_stat(ctx.cgroup, &ctx.cgroup_stats);
+               pakfire_cgroup_stat_dump(ctx.cgroup, &ctx.cgroup_stats);
                pakfire_cgroup_destroy(ctx.cgroup);
                pakfire_cgroup_unref(ctx.cgroup);
        }
@@ -2036,11 +2068,13 @@ ERROR:
        pakfire_jail_close_pipe(jail, ctx.pipes.stdin);
        pakfire_jail_close_pipe(jail, ctx.pipes.stdout);
        pakfire_jail_close_pipe(jail, ctx.pipes.stderr);
-       if (ctx.pidfd)
+       if (ctx.pidfd >= 0)
                close(ctx.pidfd);
        pakfire_jail_close_pipe(jail, ctx.pipes.log_INFO);
        pakfire_jail_close_pipe(jail, ctx.pipes.log_ERROR);
+#ifdef ENABLE_DEBUG
        pakfire_jail_close_pipe(jail, ctx.pipes.log_DEBUG);
+#endif /* ENABLE_DEBUG */
 
        return exit;
 }