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