RE: [PATCH 1/1] fport_print: handle ttyname ENODEV

Rob Browning <[email protected]>
Newsgroups gmane.lisp.guile.devel
Message-ID <[email protected]>
Rob Browning <[email protected]> writes:

> Oh, and in addition to wondering about using ttyname_r when it's
> available, if we did that, or even if we didn't, I could also see moving
> the shared code to a shared helper, but I was just starting with a
> "simpler" proposal (to avoid the crash).

Attached is a variant of the fancier approach (also relying on ttyname_r
when available) that I'd been thinking of (though might not be quite
right yet), and which I realized you probably also suggested.

The TTY_NAME_MAX comment's referring to whether or not "The maximum
length of the terminal name shall be {TTY_NAME_MAX}." here only applies
to the paragraph it's in (i.e. just for ttyname_r()), or applies to
ttyname() too:

  https://pubs.opengroup.org/onlinepubs/009696899/functions/ttyname.html


-- 
Rob Browning
rlb @defaultvalue.org and @debian.org
GPG as of 2011-07-10 E6A9 DA3C C9FD 1FF8 C676 D2C4 C0F0 39E9 ED1B 597A
GPG as of 2002-11-03 14DD 432F AE39 534D B592 F9A0 25C8 D377 8C7E 73A4
0001-fport_print-handle-ttyname-ENODEV.patch (text/x-diff, 7.3 KB)
From 94b338f144441483b0d0513be5c90d88c7aaeffb Mon Sep 17 00:00:00 2001
From: Rob Browning <[email protected]>
Date: Fri, 17 Jan 2025 11:45:26 -0600
Subject: [PATCH 1/1] fport_print: handle ttyname ENODEV

In some situations, ttyname may return ENODEV even though isatty is
true.  From ttyname(3):

  A process that keeps a file descriptor that refers to a pts(4) device
  open when switching to another mount namespace that uses a different
  /dev/ptmx instance may still accidentally find that a device path of
  the same name for that file descriptor exists.  However, this device
  path refers to a different device and thus can't be used to access the
  device that the file descriptor refers to.  Calling ttyname() or
  ttyname_r() on the file descriptor in the new mount namespace will
  cause these functions to return NULL and set errno to ENODEV.

Observed in a Debian riscv64 porterbox schroot.

When ttyname fails with ENODEV, just include the file descriptor integer
value instead.  Call ttyname() rather than scm_ttyname() to avoid some
extra work and having to catch the ENODEV.

* libguile/fports.c: include the integer fd when ttyname() fails with
ENODEV.
* libguile/posix.c: add scm_i_c_ttyname().
* libguile/posix.h: add scm_i_c_ttyname().
---
 configure.ac      |  2 +-
 libguile/fports.c | 31 ++++++++++++++++---
 libguile/posix.c  | 78 ++++++++++++++++++++++++++++-------------------
 libguile/posix.h  |  1 +
 4 files changed, 74 insertions(+), 38 deletions(-)

diff --git a/configure.ac b/configure.ac
index af9176b46..2e89f93a9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -549,7 +549,7 @@ AC_CHECK_FUNCS([DINFINITY DQNAN cexp chsize clog clog10 ctermid         \
   nice readlink rmdir setegid seteuid                            \
   setuid setgid setpgid setsid sigaction siginterrupt stat64  \
   strptime symlink sync sysconf tcgetpgrp tcsetpgrp uname waitpid       \
-  strdup usleep on_exit chown link fcntl ttyname getpwent \
+  strdup usleep on_exit chown link fcntl ttyname ttyname_r getpwent \
   getgrent kill getppid getpgrp fork setitimer getitimer strchr strcmp  \
   index bcopy rindex truncate isblank _NSGetEnviron              \
   strcoll_l strtod_l strtol_l newlocale uselocale utimensat     \
diff --git a/libguile/fports.c b/libguile/fports.c
index 9d4ca6ace..5ba09c552 100644
--- a/libguile/fports.c
+++ b/libguile/fports.c
@@ -554,6 +554,7 @@ SCM_DEFINE (scm_adjust_port_revealed_x, "adjust-port-revealed!", 2, 0, 0,
 
 
 
+#define FUNC_NAME "fport_print"
 static int 
 fport_print (SCM exp, SCM port, scm_print_state *pstate SCM_UNUSED)
 {
@@ -570,12 +571,31 @@ fport_print (SCM exp, SCM port, scm_print_state *pstate SCM_UNUSED)
       scm_putc (' ', port);
       fdes = (SCM_FSTREAM (exp))->fdes;
 
-#if (defined HAVE_TTYNAME) && (defined HAVE_POSIX)
-      if (isatty (fdes))
-	scm_display (scm_ttyname (exp), port);
-      else
-#endif /* HAVE_TTYNAME */
+#if ((defined HAVE_TTYNAME) || (defined HAVE_TTYNAME_R)) && (defined HAVE_POSIX)
+      if (!isatty (fdes))
 	scm_intprint (fdes, 10, port);
+      else
+        {
+          SCM name = scm_i_c_ttyname (fdes);
+          if (scm_is_string (name))
+            scm_display (name, port);
+          else
+            {
+              const int err = scm_to_int (name);
+              // In some situations, ttyname may return ENODEV even
+              // though isatty is true.  cf. the GNU/Linux ttyname(3).
+              if (err == ENODEV)
+                scm_intprint (fdes, 10, port);
+              else
+                {
+                  errno = err;
+                  SCM_SYSERROR;
+                }
+            }
+        }
+#else /* can't use ttyname */
+      scm_intprint (fdes, 10, port);
+#endif
     }
   else
     {
@@ -586,6 +606,7 @@ fport_print (SCM exp, SCM port, scm_print_state *pstate SCM_UNUSED)
   scm_putc ('>', port);
   return 1;
 }
+#undef FUNC_NAME
 
 /* fill a port's read-buffer with a single read.  returns the first
    char or EOF if end of file.  */
diff --git a/libguile/posix.c b/libguile/posix.c
index 0e57f012b..a9f726f98 100644
--- a/libguile/posix.c
+++ b/libguile/posix.c
@@ -1043,52 +1043,66 @@ SCM_DEFINE (scm_getsid, "getsid", 1, 0, 0,
 #endif /* HAVE_GETSID */
 
 
-/* ttyname returns its result in a single static buffer, hence
-   scm_i_misc_mutex for thread safety.  In glibc 2.3.2 two threads
-   continuously calling ttyname will otherwise get an overwrite quite
-   easily.
+#if (defined HAVE_TTYNAME) || (defined HAVE_TTYNAME_R)
 
-   ttyname_r (when available) could be used instead of scm_i_misc_mutex, but
-   there's probably little to be gained in either speed or parallelism.  */
+#define FUNC_NAME "scm_i_c_ttyname"
+SCM
+scm_i_c_ttyname (int fd)
+{
+  // Return the string ttyname or the integer errno.
+  int err = 0;
+  char name[TTY_NAME_MAX];
+
+#ifdef HAVE_TTYNAME_R
+  SCM_SYSCALL (err = ttyname_r (fd, name, TTY_NAME_MAX));
+#else // HAVE_TTYNAME
+  // ttyname returns its result in a single static buffer, hence
+  // scm_i_misc_mutex for thread safety.  In glibc 2.3.2 two threads
+  // continuously calling ttyname will otherwise get an overwrite quite
+  // easily.
+  scm_i_scm_pthread_mutex_lock (&scm_i_misc_mutex);
+  char *global_name;
+  SCM_SYSCALL (global_name = ttyname (fd));
+  err = errno;
+  if (!err)
+    {
+      // Not necessary if ttyname() must also respect TTY_NAME_MAX.
+      // POSIX ttyname description isn't completely clear.
+      if (strlen (global_name) > TTY_NAME_MAX - 1)
+        errno = ERANGE;
+      else
+        strcpy(name, global_name);
+    }
+  scm_i_pthread_mutex_unlock (&scm_i_misc_mutex);
+#endif // HAVE_TTYNAME
 
-#ifdef HAVE_TTYNAME
-SCM_DEFINE (scm_ttyname, "ttyname", 1, 0, 0, 
+  if (err)
+    return scm_from_int(err);
+  else
+    return scm_from_locale_string (name);
+}
+#undef FUNC_NAME
+
+SCM_DEFINE (scm_ttyname, "ttyname", 1, 0, 0,
             (SCM port),
 	    "Return a string with the name of the serial terminal device\n"
 	    "underlying @var{port}.")
 #define FUNC_NAME s_scm_ttyname
 {
-  char *result;
-  int fd, err;
-  SCM ret = SCM_BOOL_F;
-
   port = SCM_COERCE_OUTPORT (port);
   SCM_VALIDATE_OPPORT (1, port);
   if (!SCM_FPORTP (port))
     return SCM_BOOL_F;
-  fd = SCM_FPORT_FDES (port);
 
-  scm_i_scm_pthread_mutex_lock (&scm_i_misc_mutex);
-
-  SCM_SYSCALL (result = ttyname (fd));
-  err = errno;
-  if (result != NULL)
-    result = strdup (result);
-
-  scm_i_pthread_mutex_unlock (&scm_i_misc_mutex);
-
-  if (!result)
-    {
-      errno = err;
-      SCM_SYSERROR;
-    }
-  else
-    ret = scm_take_locale_string (result);
-
-  return ret;
+  SCM result = scm_i_c_ttyname (SCM_FPORT_FDES (port));
+  if (scm_is_string (result))
+    return result;
+  errno = scm_to_int (result);
+  SCM_SYSERROR;
 }
 #undef FUNC_NAME
-#endif /* HAVE_TTYNAME */
+
+#endif // (defined HAVE_TTYNAME) || (defined HAVE_TTYNAME_R)
 
 
 /* For thread safety "buf" is used instead of NULL for the ctermid static
diff --git a/libguile/posix.h b/libguile/posix.h
index a4b0297b3..acfaa3c70 100644
--- a/libguile/posix.h
+++ b/libguile/posix.h
@@ -91,6 +91,7 @@ SCM_API SCM scm_sethostname (SCM name);
 SCM_API SCM scm_gethostname (void);
 SCM_API SCM scm_getaffinity (SCM pid);
 SCM_API SCM scm_setaffinity (SCM pid, SCM cpu_set);
+SCM_INTERNAL SCM scm_i_c_ttyname (int fd);
 SCM_INTERNAL void scm_init_posix (void);
 
 SCM_INTERNAL scm_i_pthread_mutex_t scm_i_locale_mutex;
-- 
2.45.2
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.