From: Michael Tremer Date: Sun, 6 Oct 2024 16:57:46 +0000 (+0000) Subject: pty: Fix setting raw mode X-Git-Tag: 0.9.30~1131 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c1388a06fa69bed268c35e6a715bb027402c9cfe;p=people%2Fms%2Fpakfire.git pty: Fix setting raw mode If the standard input & output share the same inode, we can't overwrite the attributes and need to take care of this. Signed-off-by: Michael Tremer --- diff --git a/src/libpakfire/pty.c b/src/libpakfire/pty.c index 6e3d2a798..5ac956e79 100644 --- a/src/libpakfire/pty.c +++ b/src/libpakfire/pty.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -42,9 +43,7 @@ struct pakfire_pty_stdio { // Terminal Attributes struct termios attrs; - - // File Descriptor Flags - int flags; + unsigned int attrs_saved:1; // IO Flags enum pakfire_pty_io { @@ -95,16 +94,60 @@ static int pakfire_pty_has_flag(struct pakfire_pty* pty, int flag) { return pty->flags & flag; } +static int pakfire_pty_same_inode(struct pakfire_pty* pty, int fd1, int fd2) { + struct stat stat1; + struct stat stat2; + int r; + + // Stat the first file descriptor + r = fstat(fd1, &stat1); + if (r < 0) { + CTX_ERROR(pty->ctx, "Could not stat fd %d: %m\n", fd1); + return -errno; + } + + // Stat the second file descriptor + r = fstat(fd2, &stat2); + if (r < 0) { + CTX_ERROR(pty->ctx, "Could not stat fd %d: %m\n", fd2); + return -errno; + } + + // Mismatched mode + if ((stat1.st_mode ^ stat2.st_mode) & S_IFMT) + return 0; + + // Match if the device and inode are identical + return (stat1.st_dev == stat2.st_dev) && (stat1.st_ino == stat2.st_ino); +} + static int pakfire_pty_restore_attrs(struct pakfire_pty* pty, struct pakfire_pty_stdio* stdio) { + int flags; int r; + // Do nothing if the file descriptor is no longer open + if (stdio->fd < 0) + return 0; + + // Don't restore anything if we didn't save things before + if (!stdio->attrs_saved) + return 0; + // Skip everything if fd is not a TTY if (!isatty(stdio->fd)) return 0; - // Restore the flags (and make the file descriptor blocking again) - r = fcntl(stdio->fd, F_SETFL, stdio->flags & ~O_NONBLOCK); + // Fetch the flags + flags = fcntl(stdio->fd, F_GETFL, 0); + if (flags < 0) { + CTX_ERROR(pty->ctx, "Could not set flags for file descriptor %d: %s\n", + stdio->fd, strerror(errno)); + return -errno; + } + + // Turn on non-blocking mode again + r = fcntl(stdio->fd, F_SETFL, flags & ~O_NONBLOCK); if (r < 0) { CTX_ERROR(pty->ctx, "Could not set flags for file descriptor %d: %s\n", stdio->fd, strerror(errno)); @@ -126,6 +169,24 @@ static int pakfire_pty_restore_attrs(struct pakfire_pty* pty, return 0; } +static int pakfire_pty_store_attrs(struct pakfire_pty* pty, + struct pakfire_pty_stdio* stdio) { + int r; + + // Store all attributes + r = tcgetattr(stdio->fd, &stdio->attrs); + if (r) { + CTX_ERROR(pty->ctx, "Could not fetch terminal attributes from fd %d: %s\n", + stdio->fd, strerror(errno)); + return -errno; + } + + // Mark the attributes as saved + stdio->attrs_saved = 1; + + return 0; +} + static int pakfire_pty_disconnect(struct pakfire_pty* pty) { if (pty->master.fd >= 0) { close(pty->master.fd); @@ -133,10 +194,8 @@ static int pakfire_pty_disconnect(struct pakfire_pty* pty) { } // Restore any changed terminal attributes - if (pty->stdin.fd >= 0) - pakfire_pty_restore_attrs(pty, &pty->stdin); - if (pty->stdout.fd >= 0) - pakfire_pty_restore_attrs(pty, &pty->stdout); + pakfire_pty_restore_attrs(pty, &pty->stdin); + pakfire_pty_restore_attrs(pty, &pty->stdout); // Clear events if (pty->master.event) @@ -416,59 +475,66 @@ static int pakfire_pty_stdout(sd_event_source* source, int fd, uint32_t events, return pakfire_pty_activity(pty, &pty->stdout, events); } -static int pakfire_pty_enable_raw_mode(struct pakfire_pty* pty, struct pakfire_pty_stdio* stdio) { +static int pakfire_pty_enable_raw_mode(struct pakfire_pty* pty) { struct termios raw_attrs; + int same; int r; - // Skip if we don't know the file descriptor - if (stdio->fd < 0) - return 0; + CTX_DEBUG(pty->ctx, "Enabling raw mode\n"); - // Skip everything if fd is not a TTY - if (!isatty(stdio->fd)) - return 0; + r = pakfire_pty_same_inode(pty, pty->stdin.fd, pty->stdout.fd); + if (r < 0) + return r; - CTX_DEBUG(pty->ctx, "Enabling raw mode on fd %d\n", stdio->fd); + // Are standard input/output the same inode? + same = (r > 0); - // Store flags - stdio->flags = fcntl(stdio->fd, F_GETFL); - if (stdio->flags < 0) { - CTX_ERROR(pty->ctx, "Could not fetch flags from fd %d: %s\n", - stdio->fd, strerror(errno)); - return -errno; - } + // Store attributes for standard input + if (pty->stdin.fd >= 0) { + r = pakfire_pty_store_attrs(pty, &pty->stdin); + if (r < 0) + return r; - // Fetch all attributes - r = tcgetattr(stdio->fd, &stdio->attrs); - if (r) { - CTX_ERROR(pty->ctx, "Could not fetch terminal attributes from fd %d: %s\n", - stdio->fd, strerror(errno)); - return -errno; + // Copy the attributes + raw_attrs = pty->stdin.attrs; + + // Enable raw mode + cfmakeraw(&raw_attrs); + + if (!same) + raw_attrs.c_oflag = pty->stdin.attrs.c_oflag; + + // Restore the attributes + r = tcsetattr(pty->stdin.fd, TCSANOW, &raw_attrs); + if (r) { + CTX_ERROR(pty->ctx, "Could not restore terminal attributes for fd %d: %s\n", + pty->stdin.fd, strerror(errno)); + return -errno; + } } - // Copy all attributes - raw_attrs = stdio->attrs; + // Store attributes for standard output + if (!same && pty->stdout.fd >= 0) { + r = pakfire_pty_store_attrs(pty, &pty->stdout); + if (r < 0) + return r; - // Make it RAW - cfmakeraw(&raw_attrs); + // Copy the attributes + raw_attrs = pty->stdout.attrs; - switch (stdio->fd) { - case STDIN_FILENO: - raw_attrs.c_oflag = stdio->attrs.c_oflag; - break; + // Enable raw mode + cfmakeraw(&raw_attrs); - case STDOUT_FILENO: - raw_attrs.c_iflag = stdio->attrs.c_iflag; - raw_attrs.c_lflag = stdio->attrs.c_lflag; - break; - } + raw_attrs.c_iflag = pty->stdout.attrs.c_iflag; + raw_attrs.c_lflag = pty->stdout.attrs.c_lflag; - // Restore the attributes - r = tcsetattr(stdio->fd, TCSANOW, &raw_attrs); - if (r) { - CTX_ERROR(pty->ctx, "Could not restore terminal attributes for fd %d: %s\n", - stdio->fd, strerror(errno)); - return -errno; + // Restore the attributes + r = tcsetattr(pty->stdout.fd, TCSANOW, &raw_attrs); + if (r) { + CTX_ERROR(pty->ctx, "Could not restore terminal attributes for fd %d: %s\n", + pty->stdout.fd, strerror(errno)); + return -errno; + } } return 0; @@ -560,8 +626,8 @@ static int pakfire_pty_setup_forwarding(struct pakfire_pty* pty) { // Connect standard input unless we are in read-only mode if (!pakfire_pty_has_flag(pty, PAKFIRE_PTY_READ_ONLY)) { - // Enable RAW mode on standard input - r = pakfire_pty_enable_raw_mode(pty, &pty->stdin); + // Enable RAW mode + r = pakfire_pty_enable_raw_mode(pty); if (r) return r; @@ -575,11 +641,6 @@ static int pakfire_pty_setup_forwarding(struct pakfire_pty* pty) { sd_event_source_set_description(pty->stdin.event, "pty-stdin"); } - // Enable RAW mode on standard output - r = pakfire_pty_enable_raw_mode(pty, &pty->stdout); - if (r) - return r; - // Add standard output to the event loop r = sd_event_add_io(pty->loop, &pty->stdout.event, pty->stdout.fd, EPOLLOUT|EPOLLET, pakfire_pty_stdout, pty);