diff options
author | Lennart Poettering <lennart@poettering.net> | 2024-10-30 16:45:15 +0100 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2024-10-30 22:37:44 +0100 |
commit | fc9dc71a3f1aeafaada1fd327d3a228e53ed94b6 (patch) | |
tree | 9dcf12a9cdef57ce10a4bcf5c84a3b458b493765 /src/basic | |
parent | terminal-util: various minor modernizations (diff) | |
download | systemd-fc9dc71a3f1aeafaada1fd327d3a228e53ed94b6.tar.xz systemd-fc9dc71a3f1aeafaada1fd327d3a228e53ed94b6.zip |
terminal-util: add pty_open_peer() helper
This opens a pty peer in one go, and uses the new race-free TIOCGPTPEER
ioctl() to do so – if it is available.
Diffstat (limited to 'src/basic')
-rw-r--r-- | src/basic/terminal-util.c | 57 | ||||
-rw-r--r-- | src/basic/terminal-util.h | 3 |
2 files changed, 60 insertions, 0 deletions
diff --git a/src/basic/terminal-util.c b/src/basic/terminal-util.c index 65f2059420..4086888e5d 100644 --- a/src/basic/terminal-util.c +++ b/src/basic/terminal-util.c @@ -2284,3 +2284,60 @@ int terminal_is_pty_fd(int fd) { return true; } + +int pty_open_peer_racefree(int fd, int mode) { + assert(fd >= 0); + + /* Opens the peer PTY using the new race-free TIOCGPTPEER ioctl() (kernel 4.13). + * + * This is safe to be called on TTYs from other namespaces. */ + + assert((mode & (O_CREAT|O_PATH|O_DIRECTORY|O_TMPFILE)) == 0); + + /* This replicates the EIO retry logic of open_terminal() in a modified way. */ + for (unsigned c = 0;; c++) { + int peer_fd = ioctl(fd, TIOCGPTPEER, mode); + if (peer_fd >= 0) + return peer_fd; + + if (ERRNO_IS_NOT_SUPPORTED(errno) || errno == EINVAL) /* new ioctl() is not supported, return a clear error */ + return -EOPNOTSUPP; + + if (errno != EIO) + return -errno; + + /* Max 1s in total */ + if (c >= 20) + return -EIO; + + (void) usleep_safe(50 * USEC_PER_MSEC); + } +} + +int pty_open_peer(int fd, int mode) { + int r; + + assert(fd >= 0); + + /* Opens the peer PTY using the new race-free TIOCGPTPEER ioctl() (kernel 4.13) if it is + * available. Otherwise falls back to the POSIX ptsname() + open() logic. + * + * Because of the fallback path this is not safe to be called on PTYs from other namespaces. (Because + * we open the peer PTY name there via a path in the file system.) */ + + // TODO: Remove fallback path once baseline is updated to >= 4.13, i.e. systemd v258 + + int peer_fd = pty_open_peer_racefree(fd, mode); + if (peer_fd >= 0) + return peer_fd; + if (!ERRNO_IS_NEG_NOT_SUPPORTED(peer_fd)) + return peer_fd; + + /* The racy fallback path */ + _cleanup_free_ char *peer_path = NULL; + r = ptsname_malloc(fd, &peer_path); + if (r < 0) + return r; + + return open_terminal(peer_path, mode); +} diff --git a/src/basic/terminal-util.h b/src/basic/terminal-util.h index b446e547d6..bdc0f30afa 100644 --- a/src/basic/terminal-util.h +++ b/src/basic/terminal-util.h @@ -144,3 +144,6 @@ int terminal_get_size_by_dsr(int input_fd, int output_fd, unsigned *ret_rows, un int terminal_fix_size(int input_fd, int output_fd); int terminal_is_pty_fd(int fd); + +int pty_open_peer_racefree(int fd, int mode); +int pty_open_peer(int fd, int mode); |