Rob Browning <r...@defaultvalue.org> 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

>From 94b338f144441483b0d0513be5c90d88c7aaeffb Mon Sep 17 00:00:00 2001
From: Rob Browning <r...@defaultvalue.org>
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

-- 
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

Reply via email to