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