summaryrefslogtreecommitdiffstats
path: root/src/basic
diff options
context:
space:
mode:
Diffstat (limited to 'src/basic')
-rw-r--r--src/basic/terminal-util.c57
-rw-r--r--src/basic/terminal-util.h3
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);