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