]> git.ipfire.org Git - people/ms/pakfire.git/blobdiff - src/libpakfire/jail.c
jail: Store flags of stdin/stdout
[people/ms/pakfire.git] / src / libpakfire / jail.c
index 5d091c58c7dc5f699033fb59ceb2629c284b8ecf..ec3363de6303d2ec8b35243df644d8e187826864 100644 (file)
@@ -38,6 +38,7 @@
 #include <sys/timerfd.h>
 #include <sys/types.h>
 #include <sys/wait.h>
+#include <termios.h>
 
 // libnl3
 #include <net/if.h>
@@ -178,9 +179,33 @@ struct pakfire_jail_exec {
        struct pakfire_cgroup* cgroup;
        struct pakfire_cgroup_stats cgroup_stats;
 
-       // Console
-       char console[PATH_MAX];
-       int consolefd;
+       // PTY
+       struct pakfire_jail_pty {
+               // The path to the console
+               char console[PATH_MAX];
+
+               // The master fd
+               struct pakfire_jail_pty_master {
+                       int fd;
+
+                       enum pakfire_jail_pty_flags {
+                               PAKFIRE_JAIL_PTY_READY_TO_READ  = (1 << 0),
+                               PAKFIRE_JAIL_PTY_READY_TO_WRITE = (1 << 1),
+                       } flags;
+               } master;
+
+               // Standard Input
+               struct pakfire_jail_pty_stdio {
+                       int fd;
+                       char buffer[LINE_MAX];
+                       struct termios attrs;
+                       int fdflags;
+                       enum pakfire_jail_pty_flags flags;
+               } stdin;
+
+               // Standard Output
+               struct pakfire_jail_pty_stdio stdout;
+       } pty;
 };
 
 static int clone3(struct clone_args* args, size_t size) {
@@ -655,7 +680,7 @@ static int pakfire_jail_stream_stdin(struct pakfire_jail* jail,
        }
 
        // Skip if the writing pipe has already been closed
-       if (!ctx->pipes.stdin[1])
+       if (ctx->pipes.stdin[1] < 0)
                return 0;
 
        DEBUG(jail->pakfire, "Streaming standard input...\n");
@@ -838,6 +863,125 @@ static int pakfire_jail_epoll_add_fd(struct pakfire_jail* jail, int epollfd, int
        return 0;
 }
 
+// PTY Forwarding
+
+static int pakfire_jail_enable_raw_mode(struct pakfire_jail* jail,
+               struct pakfire_jail_pty_stdio* stdio) {
+       struct termios raw_attrs;
+       int r;
+
+       // Store flags
+       stdio->fdflags = fcntl(stdio->fd, F_GETFL);
+       if (stdio->fdflags < 0) {
+               CTX_ERROR(jail->ctx, "Could not fetch flags from fd %d: %s\n",
+                       stdio->fd, strerror(errno));
+               return -errno;
+       }
+
+       // Fetch all attributes
+       r = tcgetattr(stdio->fd, &stdio->attrs);
+       if (r) {
+               CTX_ERROR(jail->ctx, "Could not fetch terminal attributes from fd %d: %s\n",
+                       stdio->fd, strerror(errno));
+               return -errno;
+       }
+
+       // Copy all attributes
+       raw_attrs = stdio->attrs;
+
+       // Make it RAW
+       cfmakeraw(&raw_attrs);
+
+       // Restore the attributes
+       r = tcsetattr(stdio->fd, TCSANOW, &raw_attrs);
+       if (r) {
+               CTX_ERROR(jail->ctx, "Could not restore terminal attributes for fd %d: %s\n",
+                       stdio->fd, strerror(errno));
+               return -errno;
+       }
+
+       return 0;
+}
+
+static int pakfire_jail_restore_attrs(struct pakfire_jail* jail,
+               const struct pakfire_jail_pty_stdio* stdio) {
+       int r;
+
+       // Restore the flags
+       r = fcntl(stdio->fd, F_SETFL, stdio->fdflags);
+       if (r < 0) {
+               CTX_ERROR(jail->ctx, "Could not set flags for file descriptor %d: %s\n",
+                       stdio->fd, strerror(errno));
+               return -errno;
+       }
+
+       // Restore the attributes
+       r = tcsetattr(stdio->fd, TCSANOW, &stdio->attrs);
+       if (r) {
+               CTX_ERROR(jail->ctx, "Could not restore terminal attributes for %d, ignoring: %s\n",
+                       stdio->fd, strerror(errno));
+               return -errno;
+       }
+
+       return 0;
+}
+
+static int pakfire_jail_setup_pty_forwarding(struct pakfire_jail* jail,
+               struct pakfire_jail_exec* ctx, const int epollfd, const int fd) {
+       struct winsize size;
+       int r;
+
+       CTX_DEBUG(jail->ctx, "Setting up PTY forwarding on fd %d\n", fd);
+
+       // Store the file descriptor
+       ctx->pty.master.fd = fd;
+
+       // Configure stdin/stdout
+       ctx->pty.stdin.fd  = STDIN_FILENO;
+       ctx->pty.stdout.fd = STDOUT_FILENO;
+
+       // Fetch dimensions
+       r = ioctl(ctx->pty.stdout.fd, TIOCGWINSZ, &size);
+       if (r) {
+               CTX_ERROR(jail->ctx, "Failed to determine terminal dimensions: %s\n", strerror(errno));
+               return -errno;
+       }
+
+       // Set dimensions
+       r = ioctl(ctx->pty.master.fd, TIOCSWINSZ, &size);
+       if (r) {
+               CTX_ERROR(jail->ctx, "Failed setting dimensions: %s\n", strerror(errno));
+               return -errno;
+       }
+
+       // Enable RAW mode on standard input
+       r = pakfire_jail_enable_raw_mode(jail, &ctx->pty.stdin);
+       if (r)
+               return r;
+
+       // Enable RAW mode on standard output
+       r = pakfire_jail_enable_raw_mode(jail, &ctx->pty.stdout);
+       if (r)
+               return r;
+
+       // Add the master to the event loop
+       r = pakfire_jail_epoll_add_fd(jail, epollfd, ctx->pty.master.fd, EPOLLIN|EPOLLOUT|EPOLLET);
+       if (r)
+               return r;
+
+       // Add standard input to the event loop
+       r = pakfire_jail_epoll_add_fd(jail, epollfd, ctx->pty.stdin.fd, EPOLLIN|EPOLLET);
+       if (r)
+               return r;
+
+       // Add standard output to the event loop
+       r = pakfire_jail_epoll_add_fd(jail, epollfd, ctx->pty.stdout.fd, EPOLLOUT|EPOLLET);
+       if (r)
+               return r;
+
+       return 0;
+}
+
 static int pakfire_jail_wait(struct pakfire_jail* jail, struct pakfire_jail_exec* ctx) {
        int epollfd = -1;
        struct epoll_event events[EPOLL_MAX_EVENTS];
@@ -1036,7 +1180,14 @@ static int pakfire_jail_wait(struct pakfire_jail* jail, struct pakfire_jail_exec
                                        if (r)
                                                goto ERROR;
 
-                                       // XXX Do something with the file descriptor
+                                       // Setup PTY forwarding
+                                       if (ctx->pty.master.fd < 0) {
+                                               r = pakfire_jail_setup_pty_forwarding(jail, ctx, epollfd, fd);
+                                               if (r) {
+                                                       CTX_ERROR(jail->ctx, "Failed setting up PTY forwarding: %s\n", strerror(-r));
+                                                       goto ERROR;
+                                               }
+                                       }
 
                                        // Don't fall through to log processing
                                        continue;
@@ -1088,6 +1239,10 @@ ERROR:
        if (timerfd >= 0)
                close(timerfd);
 
+       // Restore any changed terminal attributes
+       pakfire_jail_restore_attrs(jail, &ctx->pty.stdin);
+       pakfire_jail_restore_attrs(jail, &ctx->pty.stdout);
+
        return r;
 }
 
@@ -1693,30 +1848,30 @@ 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)
+       ctx->pty.master.fd = posix_openpt(O_RDWR|O_NONBLOCK|O_NOCTTY|O_CLOEXEC);
+       if (ctx->pty.master.fd < 0)
                return -errno;
 
        // Fetch the path
-       r = ptsname_r(ctx->consolefd, ctx->console, sizeof(ctx->console));
+       r = ptsname_r(ctx->pty.master.fd, ctx->pty.console, sizeof(ctx->pty.console));
        if (r)
                return -r;
 
-       CTX_DEBUG(jail->ctx, "Allocated console at %s (%d)\n", ctx->console, ctx->consolefd);
+       CTX_DEBUG(jail->ctx, "Allocated console at %s (%d)\n", ctx->pty.console, ctx->pty.master.fd);
 
+#if 0
        // Create a symlink
-       r = pakfire_symlink(jail->ctx, "/dev/console", ctx->console);
+       r = pakfire_symlink(jail->ctx, "/dev/console", ctx->pty.console);
        if (r)
                return r;
+#endif
 
        return r;
 }
-#endif
 
 static int pakfire_jail_child(struct pakfire_jail* jail, struct pakfire_jail_exec* ctx,
                const char* argv[]) {
@@ -1782,27 +1937,13 @@ static int pakfire_jail_child(struct pakfire_jail* jail, struct pakfire_jail_exe
                return 126;
        }
 
+       const int socket_send = pakfire_jail_get_pipe_to_write(jail, &ctx->socket);
+
        // 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);
 
@@ -1862,6 +2003,36 @@ static int pakfire_jail_child(struct pakfire_jail* jail, struct pakfire_jail_exe
                }
        }
 
+#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 r;
+       }
+#endif
+
+       // 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 r;
+       }
+
+       // Send the PTY master to the parent process
+       r = pakfire_jail_send_fd(jail, socket_send, ctx->pty.master.fd);
+       if (r) {
+               CTX_ERROR(jail->ctx, "Failed sending the PTY master to the parent: %s\n", strerror(-r));
+               return r;
+       }
+
+       // Close the master of the PTY
+       close(ctx->pty.master.fd);
+       ctx->pty.master.fd = -1;
+
+       // Close the socket
+       close(socket_send);
+
        // Close other end of log pipes
        close(ctx->pipes.log_INFO[0]);
        close(ctx->pipes.log_ERROR[0]);
@@ -1995,6 +2166,19 @@ static int __pakfire_jail_exec(struct pakfire_jail* jail, const char* argv[],
                },
 
                .pidfd = -1,
+
+               // PTY
+               .pty = {
+                       .master = {
+                               .fd = -1,
+                       },
+                       .stdin = {
+                               .fd = -1,
+                       },
+                       .stdout = {
+                               .fd = -1,
+                       },
+               },
        };
 
        DEBUG(jail->pakfire, "Executing jail...\n");
@@ -2162,6 +2346,8 @@ ERROR:
        pakfire_jail_close_pipe(jail, ctx.pipes.stderr);
        if (ctx.pidfd >= 0)
                close(ctx.pidfd);
+       if (ctx.pty.master.fd >= 0)
+               close(ctx.pty.master.fd);
        pakfire_jail_close_pipe(jail, ctx.pipes.log_INFO);
        pakfire_jail_close_pipe(jail, ctx.pipes.log_ERROR);
 #ifdef ENABLE_DEBUG