]> git.ipfire.org Git - thirdparty/glibc.git/blobdiff - sysdeps/unix/sysv/linux/ttyname_r.c
Update copyright dates with scripts/update-copyrights.
[thirdparty/glibc.git] / sysdeps / unix / sysv / linux / ttyname_r.c
index eee4d862b2318d7c741a6ec2fd0dac954a340d89..d1ea0d931cc95f710a9c2454d016fa4d62fba3ed 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 1991,92,93,1995-2001, 2003 Free Software Foundation, Inc.
+/* Copyright (C) 1991-2019 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
    The GNU C Library is free software; you can redistribute it and/or
@@ -12,9 +12,8 @@
    Lesser General Public License for more details.
 
    You should have received a copy of the GNU Lesser General Public
-   License along with the GNU C Library; if not, write to the Free
-   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-   02111-1307 USA.  */
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
 
 #include <errno.h>
 #include <limits.h>
 #include <dirent.h>
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <termios.h>
 #include <unistd.h>
 #include <string.h>
 #include <stdlib.h>
 
-#include <stdio-common/_itoa.h>
+#include <_itoa.h>
+
+#include "ttyname.h"
 
 static int getttyname_r (char *buf, size_t buflen,
-                        dev_t mydev, ino64_t myino, int save,
-                        int *dostat) internal_function;
+                        const struct stat64 *mytty, int save,
+                        int *dostat);
 
 static int
-internal_function
-getttyname_r (char *buf, size_t buflen, dev_t mydev, ino64_t myino,
+attribute_compat_text_section
+getttyname_r (char *buf, size_t buflen, const struct stat64 *mytty,
              int save, int *dostat)
 {
   struct stat64 st;
@@ -50,7 +52,7 @@ getttyname_r (char *buf, size_t buflen, dev_t mydev, ino64_t myino,
     }
 
   while ((d = __readdir64 (dirstream)) != NULL)
-    if ((d->d_fileno == myino || *dostat)
+    if ((d->d_fileno == mytty->st_ino || *dostat)
        && strcmp (d->d_name, "stdin")
        && strcmp (d->d_name, "stdout")
        && strcmp (d->d_name, "stderr"))
@@ -70,12 +72,7 @@ getttyname_r (char *buf, size_t buflen, dev_t mydev, ino64_t myino,
        cp[0] = '\0';
 
        if (__xstat64 (_STAT_VER, buf, &st) == 0
-#ifdef _STATBUF_ST_RDEV
-           && S_ISCHR (st.st_mode) && st.st_rdev == mydev
-#else
-           && d->d_fileno == myino && st.st_dev == mydev
-#endif
-          )
+           && is_mytty (mytty, &st))
          {
            (void) __closedir (dirstream);
            __set_errno (save);
@@ -98,8 +95,8 @@ __ttyname_r (int fd, char *buf, size_t buflen)
   char procname[30];
   struct stat64 st, st1;
   int dostat = 0;
+  int doispty = 0;
   int save = errno;
-  int ret;
 
   /* Test for the absolute minimal size.  This makes life easier inside
      the loop.  */
@@ -115,36 +112,46 @@ __ttyname_r (int fd, char *buf, size_t buflen)
       return ERANGE;
     }
 
-  /* We try using the /proc filesystem.  */
-  *_fitoa_word (fd, __stpcpy (procname, "/proc/self/fd/"), 10, 0) = '\0';
+  /* isatty check, tcgetattr is used because it sets the correct
+     errno (EBADF resp. ENOTTY) on error.  */
+  struct termios term;
+  if (__glibc_unlikely (__tcgetattr (fd, &term) < 0))
+    return errno;
 
-  ret = __readlink (procname, buf, buflen - 1);
-  if (ret == -1 && errno == ENOENT)
-    {
-      __set_errno (EBADF);
-      return EBADF;
-    }
+  if (__fxstat64 (_STAT_VER, fd, &st) < 0)
+    return errno;
 
-  if (!__isatty (fd))
-    {
-      __set_errno (ENOTTY);
-      return ENOTTY;
-    }
+  /* We try using the /proc filesystem.  */
+  *_fitoa_word (fd, __stpcpy (procname, "/proc/self/fd/"), 10, 0) = '\0';
 
-  if (ret == -1 && errno == ENAMETOOLONG)
+  ssize_t ret = __readlink (procname, buf, buflen - 1);
+  if (__glibc_unlikely (ret == -1 && errno == ENAMETOOLONG))
     {
       __set_errno (ERANGE);
       return ERANGE;
     }
 
-  if (ret != -1 && buf[0] != '[')
+  if (__glibc_likely (ret != -1))
     {
+#define UNREACHABLE_LEN strlen ("(unreachable)")
+      if (ret > UNREACHABLE_LEN
+         && memcmp (buf, "(unreachable)", UNREACHABLE_LEN) == 0)
+       {
+         memmove (buf, buf + UNREACHABLE_LEN, ret - UNREACHABLE_LEN);
+         ret -= UNREACHABLE_LEN;
+       }
+
+      /* readlink need not terminate the string.  */
       buf[ret] = '\0';
-      return 0;
-    }
 
-  if (__fxstat64 (_STAT_VER, fd, &st) < 0)
-    return errno;
+      /* Verify readlink result, fall back on iterating through devices.  */
+      if (buf[0] == '/'
+         && __xstat64 (_STAT_VER, buf, &st1) == 0
+         && is_mytty (&st, &st1))
+       return 0;
+
+      doispty = 1;
+    }
 
   /* Prepare the result buffer.  */
   memcpy (buf, "/dev/pts/", sizeof ("/dev/pts/"));
@@ -152,13 +159,8 @@ __ttyname_r (int fd, char *buf, size_t buflen)
 
   if (__xstat64 (_STAT_VER, buf, &st1) == 0 && S_ISDIR (st1.st_mode))
     {
-#ifdef _STATBUF_ST_RDEV
-      ret = getttyname_r (buf, buflen, st.st_rdev, st.st_ino, save,
-                         &dostat);
-#else
-      ret = getttyname_r (buf, buflen, st.st_dev, st.st_ino, save,
+      ret = getttyname_r (buf, buflen, &st, save,
                          &dostat);
-#endif
     }
   else
     {
@@ -170,26 +172,26 @@ __ttyname_r (int fd, char *buf, size_t buflen)
     {
       buf[sizeof ("/dev/") - 1] = '\0';
       buflen += sizeof ("pts/") - 1;
-#ifdef _STATBUF_ST_RDEV
-      ret = getttyname_r (buf, buflen, st.st_rdev, st.st_ino, save,
+      ret = getttyname_r (buf, buflen, &st, save,
                          &dostat);
-#else
-      ret = getttyname_r (buf, buflen, st.st_dev, st.st_ino, save,
-                         &dostat);
-#endif
     }
 
   if (ret && dostat != -1)
     {
       buf[sizeof ("/dev/") - 1] = '\0';
       dostat = 1;
-#ifdef _STATBUF_ST_RDEV
-      ret = getttyname_r (buf, buflen, st.st_rdev, st.st_ino,
-                         save, &dostat);
-#else
-      ret = getttyname_r (buf, buflen, st.st_dev, st.st_ino,
+      ret = getttyname_r (buf, buflen, &st,
                          save, &dostat);
-#endif
+    }
+
+  if (ret && doispty && is_pty (&st))
+    {
+      /* We failed to figure out the TTY's name, but we can at least
+         signal that we did verify that it really is a PTY slave.
+         This happens when we have inherited the file descriptor from
+         a different mount namespace.  */
+      __set_errno (ENODEV);
+      return ENODEV;
     }
 
   return ret;