1 /* Copyright (C) 1991-2022 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 char *__ttyname;
29 
30 static char *getttyname (int fd, dev_t mydev, ino_t myino,
31 			 int save, int *dostat);
32 
33 
34 libc_freeres_ptr (static char *getttyname_name);
35 
36 static char *
getttyname(int fd,dev_t mydev,ino_t myino,int save,int * dostat)37 getttyname (int fd, dev_t mydev, ino_t myino, int save, int *dostat)
38 {
39   static const char dev[] = "/dev";
40   static size_t namelen;
41   struct stat st;
42   DIR *dirstream;
43   struct dirent *d;
44 
45   dirstream = __opendir (dev);
46   if (dirstream == NULL)
47     {
48       *dostat = -1;
49       return NULL;
50     }
51 
52   while ((d = __readdir (dirstream)) != NULL)
53     if (((ino_t) d->d_fileno == myino || *dostat)
54 	&& strcmp (d->d_name, "stdin")
55 	&& strcmp (d->d_name, "stdout")
56 	&& strcmp (d->d_name, "stderr"))
57       {
58 	size_t dlen = _D_ALLOC_NAMLEN (d);
59 	if (sizeof (dev) + dlen > namelen)
60 	  {
61 	    free (getttyname_name);
62 	    namelen = 2 * (sizeof (dev) + dlen); /* Big enough.  */
63 	    getttyname_name = malloc (namelen);
64 	    if (! getttyname_name)
65 	      {
66 		*dostat = -1;
67 		/* Perhaps it helps to free the directory stream buffer.  */
68 		(void) __closedir (dirstream);
69 		return NULL;
70 	      }
71 	    *((char *) __mempcpy (getttyname_name, dev, sizeof (dev) - 1))
72 	      = '/';
73 	  }
74 	(void) __mempcpy (&getttyname_name[sizeof (dev)], d->d_name, dlen);
75 	if (stat (getttyname_name, &st) == 0
76 #ifdef _STATBUF_ST_RDEV
77 	    && S_ISCHR (st.st_mode) && st.st_rdev == mydev
78 #else
79 	    && (ino_t) d->d_fileno == myino && st.st_dev == mydev
80 #endif
81 	   )
82 	  {
83 	    (void) __closedir (dirstream);
84 	    __ttyname = getttyname_name;
85 	    __set_errno (save);
86 	    return getttyname_name;
87 	  }
88       }
89 
90   (void) __closedir (dirstream);
91   __set_errno (save);
92   return NULL;
93 }
94 
95 /* Return the pathname of the terminal FD is open on, or NULL on errors.
96    The returned storage is good only until the next call to this function.  */
97 char *
ttyname(int fd)98 ttyname (int fd)
99 {
100   struct stat st;
101   int dostat = 0;
102   char *name;
103   int save = errno;
104 
105   if (!__isatty (fd))
106     return NULL;
107 
108   if (fstat (fd, &st) < 0)
109     return NULL;
110 
111 #ifdef _STATBUF_ST_RDEV
112   name = getttyname (fd, st.st_rdev, st.st_ino, save, &dostat);
113 #else
114   name = getttyname (fd, st.st_dev, st.st_ino, save, &dostat);
115 #endif
116 
117   if (!name && dostat != -1)
118     {
119       dostat = 1;
120 #ifdef _STATBUF_ST_RDEV
121       name = getttyname (fd, st.st_rdev, st.st_ino, save, &dostat);
122 #else
123       name = getttyname (fd, st.st_dev, st.st_ino, save, &dostat);
124 #endif
125     }
126 
127   return name;
128 }
129