1 /*
2  * Copyright 1998-2002 by Albert Cahalan; all rights resered.
3  * This file may be used subject to the terms and conditions of the
4  * GNU Library General Public License Version 2, or any later version
5  * at your option, as published by the Free Software Foundation.
6  * This program is distributed in the hope that it will be useful,
7  * but WITHOUT ANY WARRANTY; without even the implied warranty of
8  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9  * GNU Library General Public License for more details.
10  */
11 
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #include <fcntl.h>
18 #include <unistd.h>
19 #include <limits.h>
20 #include <sys/sysmacros.h>
21 #include "version.h"
22 #include "devname.h"
23 
24 #ifndef PAGE_SIZE
25 #define PAGE_SIZE sysconf(_SC_PAGE_SIZE)
26 #endif
27 
28 /* Who uses what:
29  *
30  * tty_to_dev   oldps, w (there is a fancy version in ps)
31  * dev_to_tty   oldps, top, ps
32  */
33 
34 typedef struct tty_map_node {
35 	struct tty_map_node *next;
36 	int major_number;	/* not unsigned! Ugh... */
37 	int minor_first, minor_last;
38 	char name[16];
39 	char devfs_type;
40 } tty_map_node;
41 
42 static tty_map_node *tty_map = NULL;
43 
44 /* Load /proc/tty/drivers for device name mapping use. */
load_drivers(void)45 static void load_drivers(void)
46 {
47 	char buf[10000];
48 	char *p;
49 	int fd;
50 	int bytes;
51 	fd = open("/proc/tty/drivers", O_RDONLY);
52 	if (fd == -1)
53 		goto fail;
54 	bytes = read(fd, buf, sizeof(buf) - 1);
55 	if (bytes == -1)
56 		goto fail;
57 	buf[bytes] = '\0';
58 	p = buf;
59 	while ((p = strstr(p, " /dev/"))) {
60 		tty_map_node *tmn;
61 		int len;
62 		char *end;
63 		p += 6;
64 		end = strchr(p, ' ');
65 		if (!end)
66 			continue;
67 		len = end - p;
68 		tmn = calloc(1, sizeof(tty_map_node));
69 		tmn->next = tty_map;
70 		tty_map = tmn;
71 		/* if we have a devfs type name such as /dev/tts/%d then strip the %d but
72 		   keep a flag. */
73 		if (len >= 3 && !strncmp(end - 2, "%d", 2)) {
74 			len -= 2;
75 			tmn->devfs_type = 1;
76 		}
77 		strncpy(tmn->name, p, len);
78 		p = end;	/* set p to point past the %d as well if there is one */
79 		while (*p == ' ')
80 			p++;
81 		tmn->major_number = atoi(p);
82 		p += strspn(p, "0123456789");
83 		while (*p == ' ')
84 			p++;
85 		switch (sscanf(p, "%d-%d", &tmn->minor_first, &tmn->minor_last)) {
86 		default:
87 			/* Can't finish parsing this line so we remove it from the list */
88 			tty_map = tty_map->next;
89 			free(tmn);
90 			break;
91 		case 1:
92 			tmn->minor_last = tmn->minor_first;
93 			break;
94 		case 2:
95 			break;
96 		}
97 	}
98 fail:
99 	if (fd != -1)
100 		close(fd);
101 	if (!tty_map)
102 		tty_map = (tty_map_node *) - 1;
103 }
104 
105 /* Try to guess the device name from /proc/tty/drivers info. */
driver_name(char * restrict const buf,int maj,int min)106 static int driver_name(char *restrict const buf, int maj, int min)
107 {
108 	struct stat sbuf;
109 	tty_map_node *tmn;
110 	if (!tty_map)
111 		load_drivers();
112 	if (tty_map == (tty_map_node *) - 1)
113 		return 0;
114 	tmn = tty_map;
115 	for (;;) {
116 		if (!tmn)
117 			return 0;
118 		if (tmn->major_number == maj && tmn->minor_first <= min
119 		    && tmn->minor_last >= min)
120 			break;
121 		tmn = tmn->next;
122 	}
123 	sprintf(buf, "/dev/%s%d", tmn->name, min);	/* like "/dev/ttyZZ255" */
124 	if (stat(buf, &sbuf) < 0) {
125 		if (tmn->devfs_type)
126 			return 0;
127 		sprintf(buf, "/dev/%s", tmn->name);	/* like "/dev/ttyZZ255" */
128 		if (stat(buf, &sbuf) < 0)
129 			return 0;
130 	}
131 	if (min != minor(sbuf.st_rdev))
132 		return 0;
133 	if (maj != major(sbuf.st_rdev))
134 		return 0;
135 	return 1;
136 }
137 
138 /* Try to guess the device name (useful until /proc/PID/tty is added) */
guess_name(char * restrict const buf,int maj,int min)139 static int guess_name(char *restrict const buf, int maj, int min)
140 {
141 	struct stat sbuf;
142 	int t0, t1;
143 	int tmpmin = min;
144 	switch (maj) {
145 	case 4:
146 		if (min < 64) {
147 			sprintf(buf, "/dev/tty%d", min);
148 			break;
149 		}
150 		if (min < 128) {	/* to 255 on newer systems */
151 			sprintf(buf, "/dev/ttyS%d", min - 64);
152 			break;
153 		}
154 		tmpmin = min & 0x3f;	/* FALL THROUGH */
155 	case 3:		/* /dev/[pt]ty[p-za-o][0-9a-z] is 936 */
156 		t0 = "pqrstuvwxyzabcde"[tmpmin >> 4];
157 		t1 = "0123456789abcdef"[tmpmin & 0x0f];
158 		sprintf(buf, "/dev/tty%c%c", t0, t1);
159 		break;
160 	case 17:
161 		sprintf(buf, "/dev/ttyH%d", min);
162 		break;
163 	case 19:
164 		sprintf(buf, "/dev/ttyC%d", min);
165 		break;
166 	case 22:
167 		sprintf(buf, "/dev/ttyD%d", min);
168 		break;		/* devices.txt */
169 	case 23:
170 		sprintf(buf, "/dev/ttyD%d", min);
171 		break;		/* driver code */
172 	case 24:
173 		sprintf(buf, "/dev/ttyE%d", min);
174 		break;
175 	case 32:
176 		sprintf(buf, "/dev/ttyX%d", min);
177 		break;
178 	case 43:
179 		sprintf(buf, "/dev/ttyI%d", min);
180 		break;
181 	case 46:
182 		sprintf(buf, "/dev/ttyR%d", min);
183 		break;
184 	case 48:
185 		sprintf(buf, "/dev/ttyL%d", min);
186 		break;
187 	case 57:
188 		sprintf(buf, "/dev/ttyP%d", min);
189 		break;
190 	case 71:
191 		sprintf(buf, "/dev/ttyF%d", min);
192 		break;
193 	case 75:
194 		sprintf(buf, "/dev/ttyW%d", min);
195 		break;
196 	case 78:
197 		sprintf(buf, "/dev/ttyM%d", min);
198 		break;		/* conflict */
199 	case 105:
200 		sprintf(buf, "/dev/ttyV%d", min);
201 		break;
202 	case 112:
203 		sprintf(buf, "/dev/ttyM%d", min);
204 		break;		/* conflict */
205 		/* 136 ... 143 are /dev/pts/0, /dev/pts/1, /dev/pts/2 ... */
206 	case 136 ... 143:
207 		sprintf(buf, "/dev/pts/%d", min + (maj - 136) * 256);
208 		break;
209 	case 148:
210 		sprintf(buf, "/dev/ttyT%d", min);
211 		break;
212 	case 154:
213 		sprintf(buf, "/dev/ttySR%d", min);
214 		break;
215 	case 156:
216 		sprintf(buf, "/dev/ttySR%d", min + 256);
217 		break;
218 	case 164:
219 		sprintf(buf, "/dev/ttyCH%d", min);
220 		break;
221 	case 166:
222 		sprintf(buf, "/dev/ttyACM%d", min);
223 		break;		/* bummer, 9-char */
224 	case 172:
225 		sprintf(buf, "/dev/ttyMX%d", min);
226 		break;
227 	case 174:
228 		sprintf(buf, "/dev/ttySI%d", min);
229 		break;
230 	case 188:
231 		sprintf(buf, "/dev/ttyUSB%d", min);
232 		break;		/* bummer, 9-char */
233 	default:
234 		return 0;
235 	}
236 	if (stat(buf, &sbuf) < 0)
237 		return 0;
238 	if (min != minor(sbuf.st_rdev))
239 		return 0;
240 	if (maj != major(sbuf.st_rdev))
241 		return 0;
242 	return 1;
243 }
244 
245 /* Linux 2.2 can give us filenames that might be correct.
246  * Useful names could be in /proc/PID/fd/2 (stderr, seldom redirected)
247  * and in /proc/PID/fd/255 (used by bash to remember the tty).
248  */
link_name(char * restrict const buf,int maj,int min,int pid,const char * restrict name)249 static int link_name(char *restrict const buf, int maj, int min, int pid,
250 		     const char *restrict name)
251 {
252 	struct stat sbuf;
253 	char path[32];
254 	int count;
255 	sprintf(path, "/proc/%d/%s", pid, name);	/* often permission denied */
256 	count = readlink(path, buf, PAGE_SIZE - 1);
257 	if (count == -1)
258 		return 0;
259 	buf[count] = '\0';
260 	if (stat(buf, &sbuf) < 0)
261 		return 0;
262 	if (min != minor(sbuf.st_rdev))
263 		return 0;
264 	if (maj != major(sbuf.st_rdev))
265 		return 0;
266 	return 1;
267 }
268 
269 /* number --> name */
dev_to_tty(char * restrict ret,unsigned chop,int dev,int pid,unsigned int flags)270 unsigned dev_to_tty(char *restrict ret, unsigned chop, int dev, int pid,
271 		    unsigned int flags)
272 {
273 	static char buf[PAGE_SIZE];
274 	char *restrict tmp = buf;
275 	unsigned i = 0;
276 	int c;
277 	if ((short)dev == (short)0)
278 		goto no_tty;
279 	if (linux_version_code > LINUX_VERSION(2, 7, 0)) {	// not likely to make 2.6.xx
280 		if (link_name(tmp, major(dev), minor(dev), pid, "tty"))
281 			goto abbrev;
282 	}
283 	if (driver_name(tmp, major(dev), minor(dev)))
284 		goto abbrev;
285 	if (link_name(tmp, major(dev), minor(dev), pid, "fd/2"))
286 		goto abbrev;
287 	if (guess_name(tmp, major(dev), minor(dev)))
288 		goto abbrev;
289 	if (link_name(tmp, major(dev), minor(dev), pid, "fd/255"))
290 		goto abbrev;
291 	// fall through if unable to find a device file
292 no_tty:
293 	strcpy(ret, "?");
294 	return 1;
295 abbrev:
296 	if ((flags & ABBREV_DEV) && !strncmp(tmp, "/dev/", 5) && tmp[5])
297 		tmp += 5;
298 	if ((flags & ABBREV_TTY) && !strncmp(tmp, "tty", 3) && tmp[3])
299 		tmp += 3;
300 	if ((flags & ABBREV_PTS) && !strncmp(tmp, "pts/", 4) && tmp[4])
301 		tmp += 4;
302 	/* gotta check before we chop or we may chop someone else's memory */
303 	if (chop + (unsigned long)(tmp - buf) <= sizeof buf)
304 		tmp[chop] = '\0';
305 	/* replace non-ASCII characters with '?' and return the number of chars */
306 	for (;;) {
307 		c = *tmp;
308 		tmp++;
309 		if (!c)
310 			break;
311 		i++;
312 		if (c <= ' ')
313 			c = '?';
314 		if (c > 126)
315 			c = '?';
316 		*ret = c;
317 		ret++;
318 	}
319 	*ret = '\0';
320 	return i;
321 }
322 
323 /* name --> number */
tty_to_dev(const char * restrict const name)324 int tty_to_dev(const char *restrict const name)
325 {
326 	struct stat sbuf;
327 	static char buf[32];
328 	if (name[0] == '/' && stat(name, &sbuf) >= 0)
329 		return sbuf.st_rdev;
330 	snprintf(buf, 32, "/dev/%s", name);
331 	if (stat(buf, &sbuf) >= 0)
332 		return sbuf.st_rdev;
333 	snprintf(buf, 32, "/dev/tty%s", name);
334 	if (stat(buf, &sbuf) >= 0)
335 		return sbuf.st_rdev;
336 	snprintf(buf, 32, "/dev/pts/%s", name);
337 	if (stat(buf, &sbuf) >= 0)
338 		return sbuf.st_rdev;
339 	return -1;
340 }
341