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