1  /* Copyright (C) 1991-2023 Free Software Foundation, Inc.
       2     This file is part of the GNU C Library.
       3  
       4     The GNU C Library is free software; you can redistribute it and/or
       5     modify it under the terms of the GNU Lesser General Public
       6     License as published by the Free Software Foundation; either
       7     version 2.1 of the License, or (at your option) any later version.
       8  
       9     The GNU C Library is distributed in the hope that it will be useful,
      10     but WITHOUT ANY WARRANTY; without even the implied warranty of
      11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      12     Lesser General Public License for more details.
      13  
      14     You should have received a copy of the GNU Lesser General Public
      15     License along with the GNU C Library; if not, see
      16     <https://www.gnu.org/licenses/>.  */
      17  
      18  #include <errno.h>
      19  #include <limits.h>
      20  #include <stddef.h>
      21  #include <dirent.h>
      22  #include <sys/types.h>
      23  #include <sys/stat.h>
      24  #include <unistd.h>
      25  #include <string.h>
      26  #include <stdlib.h>
      27  
      28  #ifndef MIN
      29  # define MIN(a, b) ((a) < (b) ? (a) : (b))
      30  #endif
      31  
      32  static const char dev[] = "/dev";
      33  
      34  static int getttyname_r (int fd, char *buf, size_t buflen,
      35  			 dev_t mydev, ino_t myino, int save,
      36  			 int *dostat) __THROW;
      37  
      38  static int
      39  getttyname_r (int fd, char *buf, size_t buflen, dev_t mydev, ino_t myino,
      40  	      int save, int *dostat)
      41  {
      42    struct stat st;
      43    DIR *dirstream;
      44    struct dirent *d;
      45  
      46    dirstream = __opendir (dev);
      47    if (dirstream == NULL)
      48      {
      49        *dostat = -1;
      50        return errno;
      51      }
      52  
      53    while ((d = __readdir (dirstream)) != NULL)
      54      if (((ino_t) d->d_fileno == myino || *dostat)
      55  	&& strcmp (d->d_name, "stdin")
      56  	&& strcmp (d->d_name, "stdout")
      57  	&& strcmp (d->d_name, "stderr"))
      58        {
      59  	char *cp;
      60  	size_t needed = _D_EXACT_NAMLEN (d) + 1;
      61  
      62  	if (needed > buflen)
      63  	  {
      64  	    *dostat = -1;
      65  	    (void) __closedir (dirstream);
      66  	    __set_errno (ERANGE);
      67  	    return ERANGE;
      68  	  }
      69  
      70  	cp = __stpncpy (&buf[sizeof (dev)], d->d_name, needed);
      71  	cp[0] = '\0';
      72  
      73  	if (stat (buf, &st) == 0
      74  #ifdef _STATBUF_ST_RDEV
      75  	    && S_ISCHR (st.st_mode) && st.st_rdev == mydev
      76  #else
      77  	    && (ino_t) d->d_fileno == myino && st.st_dev == mydev
      78  #endif
      79  	   )
      80  	  {
      81  	    (void) __closedir (dirstream);
      82  	    __set_errno (save);
      83  	    return 0;
      84  	  }
      85        }
      86  
      87    (void) __closedir (dirstream);
      88    __set_errno (save);
      89    /* It is not clear what to return in this case.  `isatty' says FD
      90       refers to a TTY but no entry in /dev has this inode.  */
      91    return ENOTTY;
      92  }
      93  
      94  /* Store at most BUFLEN character of the pathname of the terminal FD is
      95     open on in BUF.  Return 0 on success,  otherwise an error number.  */
      96  int
      97  __ttyname_r (int fd, char *buf, size_t buflen)
      98  {
      99    struct stat st;
     100    int dostat = 0;
     101    int save = errno;
     102    int ret;
     103  
     104    /* Test for the absolute minimal size.  This makes life easier inside
     105       the loop.  */
     106    if (!buf)
     107      {
     108        __set_errno (EINVAL);
     109        return EINVAL;
     110      }
     111  
     112    if (buflen < (int) (sizeof (dev) + 1))
     113      {
     114        __set_errno (ERANGE);
     115        return ERANGE;
     116      }
     117  
     118    if (!__isatty (fd))
     119      {
     120        __set_errno (ENOTTY);
     121        return ENOTTY;
     122      }
     123  
     124    if (fstat (fd, &st) < 0)
     125      return errno;
     126  
     127    /* Prepare the result buffer.  */
     128    memcpy (buf, dev, sizeof (dev) - 1);
     129    buf[sizeof (dev) - 1] = '/';
     130    buflen -= sizeof (dev);
     131  
     132  #ifdef _STATBUF_ST_RDEV
     133    ret = getttyname_r (fd, buf, buflen, st.st_rdev, st.st_ino, save,
     134  		      &dostat);
     135  #else
     136    ret = getttyname_r (fd, buf, buflen, st.st_dev, st.st_ino, save,
     137  		      &dostat);
     138  #endif
     139  
     140    if (ret && dostat != -1)
     141      {
     142        dostat = 1;
     143  #ifdef _STATBUF_ST_RDEV
     144        ret = getttyname_r (fd, buf, buflen, st.st_rdev, st.st_ino,
     145  			  save, &dostat);
     146  #else
     147        ret = getttyname_r (fd, buf, buflen, st.st_dev, st.st_ino,
     148  			  save, &dostat);
     149  #endif
     150      }
     151  
     152    return ret;
     153  }
     154  
     155  weak_alias (__ttyname_r, ttyname_r)