]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
basic/terminal-util: add a heuristic check whether terminfo file exists
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Mon, 19 May 2025 13:50:42 +0000 (15:50 +0200)
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Thu, 29 May 2025 17:20:31 +0000 (19:20 +0200)
src/basic/terminal-util.c
src/basic/terminal-util.h
src/test/test-terminal-util.c

index 4b1b0ecf6c8d8fd085759a7c449ea86c2301cb5e..9f34148d2a576d980061f6bbde5c0596f9dc0692 100644 (file)
@@ -2584,6 +2584,57 @@ finish:
         return r;
 }
 
+int have_terminfo_file(const char *name) {
+        /* This is a heuristic check if we have the file, using the directory layout used on
+         * current Linux systems. Checks for other layouts can be added later if appropriate. */
+        int r;
+
+        assert(filename_is_valid(name));
+
+        _cleanup_free_ char *p = path_join("/usr/share/terminfo", CHAR_TO_STR(name[0]), name);
+        if (!p)
+                return log_oom_debug();
+
+        r = RET_NERRNO(access(p, F_OK));
+        if (r == -ENOENT)
+                return false;
+        if (r < 0)
+                return r;
+        return true;
+}
+
+int query_term_for_tty(const char *tty, char **ret_term) {
+        _cleanup_free_ char *dcs_term = NULL;
+        int r;
+
+        assert(tty);
+        assert(ret_term);
+
+        if (tty_is_vc_resolve(tty))
+                return strdup_to(ret_term, "linux");
+
+        /* Try to query the terminal implementation that we're on. This will not work in all
+         * cases, which is fine, since this is intended to be used as a fallback. */
+
+        _cleanup_close_ int tty_fd = open_terminal(tty, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
+        if (tty_fd < 0)
+                return log_debug_errno(tty_fd, "Failed to open %s to query terminfo: %m", tty);
+
+        r = terminal_get_terminfo_by_dcs(tty_fd, &dcs_term);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to query %s for terminfo: %m", tty);
+
+        r = have_terminfo_file(dcs_term);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to look for terminfo %s: %m", dcs_term);
+        if (r == 0)
+                return log_info_errno(SYNTHETIC_ERRNO(ENODATA),
+                                      "Terminfo %s not found for %s.", dcs_term, tty);
+
+        *ret_term = TAKE_PTR(dcs_term);
+        return 0;
+}
+
 int terminal_is_pty_fd(int fd) {
         int r;
 
index 79683f5a85741668e86142c01c86bdf4cccc4203..688d6808a6791cf6d30ead778d56198576e3b4a5 100644 (file)
@@ -148,6 +148,8 @@ 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_get_terminfo_by_dcs(int fd, char **ret_name);
+int have_terminfo_file(const char *name);
+int query_term_for_tty(const char *tty, char **ret_term);
 
 int terminal_is_pty_fd(int fd);
 
index ba045bedce441375db93161c02a8210295429dc1..6ab3c7fa1641a2f35cc56b4806bc7408e5902e81 100644 (file)
@@ -242,6 +242,35 @@ TEST(terminal_get_terminfo_by_dcs) {
         log_info("terminal terminfo via DCS: %s, $TERM: %s", name, strnull(getenv("TERM")));
 }
 
+TEST(have_terminfo_file) {
+        int r;
+
+        FOREACH_STRING(s,
+                       "linux",
+                       "xterm",
+                       "vt220",
+                       "xterm-256color",
+                       "nosuchfile") {
+                r = have_terminfo_file(s);
+                log_info("%s: %s → %s", __func__+5, s, r >= 0 ? yes_no(r) : STRERROR(r));
+                ASSERT_OK(r);
+        }
+}
+
+TEST(query_term_for_tty) {
+        int r;
+
+        FOREACH_STRING(s,
+                       "/dev/console",
+                       "/dev/stdin",
+                       "/dev/stdout") {
+                _cleanup_free_ char *term = NULL;
+
+                r = query_term_for_tty(s, &term);
+                log_info("%s: %s → %s/%s", __func__+5, s, STRERROR(r), strnull(term));
+        }
+}
+
 TEST(terminal_is_pty_fd) {
         _cleanup_close_ int fd1 = -EBADF, fd2 = -EBADF;
         int r;