Prefer https to http for gnu.org and fsf.org URLs
[platform/upstream/glibc.git] / sysdeps / unix / sysv / linux / ttyname_r.c
1 /* Copyright (C) 1991-2019 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 <termios.h>
25 #include <unistd.h>
26 #include <string.h>
27 #include <stdlib.h>
28
29 #include <_itoa.h>
30
31 #include "ttyname.h"
32
33 static int getttyname_r (char *buf, size_t buflen,
34                          const struct stat64 *mytty, int save,
35                          int *dostat);
36
37 static int
38 attribute_compat_text_section
39 getttyname_r (char *buf, size_t buflen, const struct stat64 *mytty,
40               int save, int *dostat)
41 {
42   struct stat64 st;
43   DIR *dirstream;
44   struct dirent64 *d;
45   size_t devlen = strlen (buf);
46
47   dirstream = __opendir (buf);
48   if (dirstream == NULL)
49     {
50       *dostat = -1;
51       return errno;
52     }
53
54   while ((d = __readdir64 (dirstream)) != NULL)
55     if ((d->d_fileno == mytty->st_ino || *dostat)
56         && strcmp (d->d_name, "stdin")
57         && strcmp (d->d_name, "stdout")
58         && strcmp (d->d_name, "stderr"))
59       {
60         char *cp;
61         size_t needed = _D_EXACT_NAMLEN (d) + 1;
62
63         if (needed > buflen)
64           {
65             *dostat = -1;
66             (void) __closedir (dirstream);
67             __set_errno (ERANGE);
68             return ERANGE;
69           }
70
71         cp = __stpncpy (buf + devlen, d->d_name, needed);
72         cp[0] = '\0';
73
74         if (__xstat64 (_STAT_VER, buf, &st) == 0
75             && is_mytty (mytty, &st))
76           {
77             (void) __closedir (dirstream);
78             __set_errno (save);
79             return 0;
80           }
81       }
82
83   (void) __closedir (dirstream);
84   __set_errno (save);
85   /* It is not clear what to return in this case.  `isatty' says FD
86      refers to a TTY but no entry in /dev has this inode.  */
87   return ENOTTY;
88 }
89
90 /* Store at most BUFLEN character of the pathname of the terminal FD is
91    open on in BUF.  Return 0 on success,  otherwise an error number.  */
92 int
93 __ttyname_r (int fd, char *buf, size_t buflen)
94 {
95   char procname[30];
96   struct stat64 st, st1;
97   int dostat = 0;
98   int doispty = 0;
99   int save = errno;
100
101   /* Test for the absolute minimal size.  This makes life easier inside
102      the loop.  */
103   if (!buf)
104     {
105       __set_errno (EINVAL);
106       return EINVAL;
107     }
108
109   if (buflen < sizeof ("/dev/pts/"))
110     {
111       __set_errno (ERANGE);
112       return ERANGE;
113     }
114
115   /* isatty check, tcgetattr is used because it sets the correct
116      errno (EBADF resp. ENOTTY) on error.  */
117   struct termios term;
118   if (__glibc_unlikely (__tcgetattr (fd, &term) < 0))
119     return errno;
120
121   if (__fxstat64 (_STAT_VER, fd, &st) < 0)
122     return errno;
123
124   /* We try using the /proc filesystem.  */
125   *_fitoa_word (fd, __stpcpy (procname, "/proc/self/fd/"), 10, 0) = '\0';
126
127   ssize_t ret = __readlink (procname, buf, buflen - 1);
128   if (__glibc_unlikely (ret == -1 && errno == ENAMETOOLONG))
129     {
130       __set_errno (ERANGE);
131       return ERANGE;
132     }
133
134   if (__glibc_likely (ret != -1))
135     {
136 #define UNREACHABLE_LEN strlen ("(unreachable)")
137       if (ret > UNREACHABLE_LEN
138           && memcmp (buf, "(unreachable)", UNREACHABLE_LEN) == 0)
139         {
140           memmove (buf, buf + UNREACHABLE_LEN, ret - UNREACHABLE_LEN);
141           ret -= UNREACHABLE_LEN;
142         }
143
144       /* readlink need not terminate the string.  */
145       buf[ret] = '\0';
146
147       /* Verify readlink result, fall back on iterating through devices.  */
148       if (buf[0] == '/'
149           && __xstat64 (_STAT_VER, buf, &st1) == 0
150           && is_mytty (&st, &st1))
151         return 0;
152
153       doispty = 1;
154     }
155
156   /* Prepare the result buffer.  */
157   memcpy (buf, "/dev/pts/", sizeof ("/dev/pts/"));
158   buflen -= sizeof ("/dev/pts/") - 1;
159
160   if (__xstat64 (_STAT_VER, buf, &st1) == 0 && S_ISDIR (st1.st_mode))
161     {
162       ret = getttyname_r (buf, buflen, &st, save,
163                           &dostat);
164     }
165   else
166     {
167       __set_errno (save);
168       ret = ENOENT;
169     }
170
171   if (ret && dostat != -1)
172     {
173       buf[sizeof ("/dev/") - 1] = '\0';
174       buflen += sizeof ("pts/") - 1;
175       ret = getttyname_r (buf, buflen, &st, save,
176                           &dostat);
177     }
178
179   if (ret && dostat != -1)
180     {
181       buf[sizeof ("/dev/") - 1] = '\0';
182       dostat = 1;
183       ret = getttyname_r (buf, buflen, &st,
184                           save, &dostat);
185     }
186
187   if (ret && doispty && is_pty (&st))
188     {
189       /* We failed to figure out the TTY's name, but we can at least
190          signal that we did verify that it really is a PTY slave.
191          This happens when we have inherited the file descriptor from
192          a different mount namespace.  */
193       __set_errno (ENODEV);
194       return ENODEV;
195     }
196
197   return ret;
198 }
199
200 weak_alias (__ttyname_r, ttyname_r)