]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
terminal-util: add terminal_is_pty_fd() helper
authorLennart Poettering <lennart@poettering.net>
Wed, 10 Jul 2024 16:52:42 +0000 (18:52 +0200)
committerLennart Poettering <lennart@poettering.net>
Fri, 19 Jul 2024 09:41:43 +0000 (11:41 +0200)
The helper checks if an fd references a pty

src/basic/terminal-util.c
src/basic/terminal-util.h
src/test/test-terminal-util.c

index 54901ccada60e214aaa66d126bcd423620b4277d..4c4a715ff66581c2084b17f042f5ae9242fbcaf2 100644 (file)
@@ -32,6 +32,7 @@
 #include "io-util.h"
 #include "log.h"
 #include "macro.h"
+#include "missing_magic.h"
 #include "namespace-util.h"
 #include "parse-util.h"
 #include "path-util.h"
@@ -2117,3 +2118,31 @@ int terminal_fix_size(int input_fd, int output_fd) {
         log_debug("Fixed terminal dimensions to %ux%u based on ANSI sequence information.", columns, rows);
         return 1;
 }
+
+int terminal_is_pty_fd(int fd) {
+        int r;
+
+        assert(fd >= 0);
+
+        /* Returns true if we are looking at a pty, i.e. if it's backed by the /dev/pts/ file system */
+
+        if (!isatty_safe(fd))
+                return false;
+
+        r = is_fs_type_at(fd, NULL, DEVPTS_SUPER_MAGIC);
+        if (r != 0)
+                return r;
+
+        /* The ptmx device is weird, it exists twice, once inside and once outside devpts. To detect the
+         * latter case, let's fire off an ioctl() that only works on ptmx devices. */
+
+        int v;
+        if (ioctl(fd, TIOCGPKT, &v) < 0) {
+                if (ERRNO_IS_NOT_SUPPORTED(errno))
+                        return false;
+
+                return -errno;
+        }
+
+        return true;
+}
index a9d5156451037d0a1c14a5f5c92be3e41e76f6a0..df28af02d504cec64d92a6a5f623021c4409c013 100644 (file)
@@ -299,3 +299,5 @@ int get_default_background_color(double *ret_red, double *ret_green, double *ret
 int terminal_get_size_by_dsr(int input_fd, int output_fd, unsigned *ret_rows, unsigned *ret_columns);
 
 int terminal_fix_size(int input_fd, int output_fd);
+
+int terminal_is_pty_fd(int fd);
index 9a89f55c80e5777317858b8118367a908dba199a..2f09ae9f71c8bd20bb44ac082852d330e7251ad0 100644 (file)
@@ -209,6 +209,46 @@ TEST(terminal_fix_size) {
                 log_notice("Fixed terminal size.");
 }
 
+TEST(terminal_is_pty_fd) {
+        _cleanup_close_ int fd1 = -EBADF, fd2 = -EBADF;
+        _cleanup_free_ char *peer = NULL;
+        int r;
+
+        fd1 = openpt_allocate(O_RDWR, &peer);
+        assert_se(fd1 >= 0);
+        assert_se(terminal_is_pty_fd(fd1) > 0);
+
+        fd2 = open_terminal(peer, O_RDWR|O_CLOEXEC|O_NOCTTY);
+        assert_se(fd2 >= 0);
+        assert_se(terminal_is_pty_fd(fd2) > 0);
+
+        fd1 = safe_close(fd1);
+        fd2 = safe_close(fd2);
+
+        fd1 = open("/dev/null", O_RDONLY|O_CLOEXEC);
+        assert_se(fd1 >= 0);
+        assert_se(terminal_is_pty_fd(fd1) == 0);
+
+        /* In container managers real tty devices might be weird, avoid them. */
+        r = path_is_read_only_fs("/sys");
+        if (r != 0)
+                return;
+
+        FOREACH_STRING(p, "/dev/ttyS0", "/dev/tty1") {
+                _cleanup_close_ int tfd = -EBADF;
+
+                tfd = open_terminal(p, O_CLOEXEC|O_NOCTTY|O_RDONLY|O_NONBLOCK);
+                if (tfd == -ENOENT)
+                        continue;
+                if (tfd < 0)  {
+                        log_notice_errno(tfd, "Failed to open '%s', skipping: %m", p);
+                        continue;
+                }
+
+                assert_se(terminal_is_pty_fd(tfd) <= 0);
+        }
+}
+
 static void test_get_color_mode_with_env(const char *key, const char *val, ColorMode expected) {
         ASSERT_OK(setenv(key, val, true));
         reset_terminal_feature_caches();