1 /*
2 * Copyright (C) Jan 1, 2004 Manuel Novoa III
3 * Copyright (C) 2000-2006 Erik Andersen <andersen@uclibc.org>
4 *
5 * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball.
6 */
7
8 /*
9 * Kept the same approach, but rewrote the code for the most part.
10 * Fixed some minor issues plus (as I recall) one SUSv3 errno case.
11 */
12
13 /* This is a fairly slow approach. We do a linear search through some
14 * directories looking for a match. Yes this is lame. But it should
15 * work, should be small, and will return names that match what is on
16 * disk. Another approach we could use would be to use the info in
17 * /proc/self/fd, but that is even more lame since it requires /proc */
18
19 /* SUSv3 mandates TTY_NAME_MAX as 9. This is obviously insufficient.
20 * However, there is no need to waste space and support non-standard
21 * tty names either. So we compromise and use the following buffer
22 * length. (Erik and Manuel agreed that 32 was more than reasonable.)
23 *
24 * If you change this, also change _SC_TTY_NAME_MAX in libc/unistd/sysconf.c
25 */
26
27 #include <string.h>
28 #include <errno.h>
29 #include <assert.h>
30 #include <unistd.h>
31 #include <dirent.h>
32 #include <sys/stat.h>
33
34 #define STAT stat64
35 #define FSTAT fstat64
36 #define LSTAT lstat64
37
38 #define TTYNAME_BUFLEN 32
39
40 static const char dirlist[] =
41 /* 12345670123 */
42 "\010/dev/vc/\0" /* Try /dev/vc first (be devfs compatible) */
43 "\011/dev/tts/\0" /* and /dev/tts next (be devfs compatible) */
44 "\011/dev/pty/\0" /* and /dev/pty next (be devfs compatible) */
45 "\011/dev/pts/\0" /* and try /dev/pts next */
46 "\005/dev/\0"; /* and try walking through /dev last */
47
ttyname_r(int fd,char * ubuf,size_t ubuflen)48 int ttyname_r(int fd, char *ubuf, size_t ubuflen)
49 {
50 struct dirent *d;
51 struct STAT st;
52 struct STAT dst;
53 const char *p;
54 char *s;
55 DIR *fp;
56 int rv;
57 size_t len;
58 char buf[TTYNAME_BUFLEN];
59
60 if (FSTAT(fd, &st) < 0) {
61 return errno;
62 }
63
64 rv = ENOTTY; /* Set up the default return value. */
65
66 if (!isatty(fd)) {
67 goto DONE;
68 }
69
70 for (p = dirlist ; *p ; p += 1 + p[-1]) {
71 len = *p++;
72
73 assert(len + 2 <= TTYNAME_BUFLEN); /* dirname + 1 char + nul */
74
75 strcpy(buf, p);
76 s = buf + len;
77 len = (TTYNAME_BUFLEN-2) - len; /* Available non-nul space. */
78
79 if (!(fp = opendir(p))) {
80 continue;
81 }
82
83 while ((d = readdir(fp)) != NULL) {
84 /* This should never trigger for standard names, but we
85 * check it to be safe. */
86 if (strlen(d->d_name) > len) { /* Too big? */
87 continue;
88 }
89
90 strcpy(s, d->d_name);
91
92 if ((LSTAT(buf, &dst) == 0)
93 #if 0
94 /* Stupid filesystems like cramfs fail to guarantee that
95 * st_ino and st_dev uniquely identify a file, contrary to
96 * SuSv3, so we cannot be quite so precise as to require an
97 * exact match. Settle for something less... Grumble... */
98 && (st.st_dev == dst.st_dev) && (st.st_ino == dst.st_ino)
99 #else
100 && S_ISCHR(dst.st_mode) && (st.st_rdev == dst.st_rdev)
101 #endif
102 ) { /* Found it! */
103 closedir(fp);
104
105 /* We treat NULL buf as ERANGE rather than EINVAL. */
106 rv = ERANGE;
107 if (ubuf && (strlen(buf) <= ubuflen)) {
108 strcpy(ubuf, buf);
109 rv = 0;
110 }
111 goto DONE;
112 }
113 }
114
115 closedir(fp);
116 }
117
118 DONE:
119 __set_errno(rv);
120
121 return rv;
122 }
libc_hidden_def(ttyname_r)123 libc_hidden_def(ttyname_r)
124
125 char *ttyname(int fd)
126 {
127 static char name[TTYNAME_BUFLEN];
128
129 return ttyname_r(fd, name, TTYNAME_BUFLEN) ? NULL : name;
130 }
131