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.
15 #include <sys/types.h>
22 // This is the buffer size for a tty name. Any path is legal,
23 // which makes PAGE_SIZE appropriate (see kernel source), but
24 // that is only 99% portable and utmp only holds 32 anyway.
25 // We need at least 20 for guess_name().
26 #define TTY_NAME_SIZE 128
30 * tty_to_dev w (there is a fancy version in ps)
35 #include <sys/sysmacros.h>
36 #define MAJOR_OF(d) ((unsigned)major(d))
37 #define MINOR_OF(d) ((unsigned)minor(d))
39 #define MAJOR_OF(d) ( ((unsigned)(d)>>8u) & 0xfffu )
40 #define MINOR_OF(d) ( ((unsigned)(d)&0xffu) | (((unsigned)(d)&0xfff00000u)>>12u) )
43 #define major <-- do not use -->
44 #define minor <-- do not use -->
47 typedef struct tty_map_node {
48 struct tty_map_node *next;
49 unsigned short devfs_type; // bool
50 unsigned short major_number;
56 static tty_map_node *tty_map = NULL;
58 /* Load /proc/tty/drivers for device name mapping use. */
59 static void load_drivers(void){
64 fd = open("/proc/tty/drivers",O_RDONLY);
65 if(fd == -1) goto fail;
66 bytes = read(fd, buf, sizeof(buf) - 1);
67 if(bytes == -1) goto fail;
70 while(( p = strstr(p, " /dev/") )){ // " /dev/" is the second column
78 tmn = calloc(1, sizeof(tty_map_node));
81 /* if we have a devfs type name such as /dev/tts/%d then strip the %d but
83 if(len >= 3 && !strncmp(end - 2, "%d", 2)){
87 if(len >= sizeof tmn->name)
88 len = sizeof tmn->name - 1; // mangle it to avoid overflow
89 memcpy(tmn->name, p, len);
90 p = end; /* set p to point past the %d as well if there is one */
92 tmn->major_number = atoi(p);
93 p += strspn(p, "0123456789");
95 switch(sscanf(p, "%u-%u", &tmn->minor_first, &tmn->minor_last)){
97 /* Can't finish parsing this line so we remove it from the list */
98 tty_map = tty_map->next;
102 tmn->minor_last = tmn->minor_first;
109 if(fd != -1) close(fd);
110 if(!tty_map) tty_map = (tty_map_node *)-1;
113 /* Try to guess the device name from /proc/tty/drivers info. */
114 static int driver_name(char *restrict const buf, unsigned maj, unsigned min){
117 if(!tty_map) load_drivers();
118 if(tty_map == (tty_map_node *)-1) return 0;
122 if(tmn->major_number == maj && tmn->minor_first <= min && tmn->minor_last >= min) break;
125 sprintf(buf, "/dev/%s%d", tmn->name, min); /* like "/dev/ttyZZ255" */
126 if(stat(buf, &sbuf) < 0){
127 if(tmn->devfs_type) return 0;
128 sprintf(buf, "/dev/%s", tmn->name); /* like "/dev/ttyZZ255" */
129 if(stat(buf, &sbuf) < 0) return 0;
131 if(min != MINOR_OF(sbuf.st_rdev)) return 0;
132 if(maj != MAJOR_OF(sbuf.st_rdev)) return 0;
136 // major 204 is a mess -- "Low-density serial ports"
137 static const char low_density_names[][6] = {
138 "LU0", "LU1", "LU2", "LU3",
141 "SC0", "SC1", "SC2", "SC3",
142 "FW0", "FW1", "FW2", "FW3",
143 "AM0", "AM1", "AM2", "AM3", "AM4", "AM5", "AM6", "AM7",
144 "AM8", "AM9", "AM10", "AM11", "AM12", "AM13", "AM14", "AM15",
145 "DB0", "DB1", "DB2", "DB3", "DB4", "DB5", "DB6", "DB7",
147 "SMX0", "SMX1", "SMX2",
149 "CPM0", "CPM1", "CPM2", "CPM3", /* "CPM4", "CPM5", */ // bad allocation?
150 "IOC0", "IOC1", "IOC2", "IOC3", "IOC4", "IOC5", "IOC6", "IOC7",
151 "IOC8", "IOC9", "IOC10", "IOC11", "IOC12", "IOC13", "IOC14", "IOC15",
152 "IOC16", "IOC17", "IOC18", "IOC19", "IOC20", "IOC21", "IOC22", "IOC23",
153 "IOC24", "IOC25", "IOC26", "IOC27", "IOC28", "IOC29", "IOC30", "IOC31",
155 "IOC84", "IOC85", "IOC86", "IOC87", "IOC88", "IOC89", "IOC90", "IOC91",
156 "IOC92", "IOC93", "IOC94", "IOC95", "IOC96", "IOC97", "IOC98", "IOC99",
157 "IOC100", "IOC101", "IOC102", "IOC103", "IOC104", "IOC105", "IOC106", "IOC107",
158 "IOC108", "IOC109", "IOC110", "IOC111", "IOC112", "IOC113", "IOC114", "IOC115",
159 "SIOC0", "SIOC1", "SIOC2", "SIOC3", "SIOC4", "SIOC5", "SIOC6", "SIOC7",
160 "SIOC8", "SIOC9", "SIOC10", "SIOC11", "SIOC12", "SIOC13", "SIOC14", "SIOC15",
161 "SIOC16", "SIOC17", "SIOC18", "SIOC19", "SIOC20", "SIOC21", "SIOC22", "SIOC23",
162 "SIOC24", "SIOC25", "SIOC26", "SIOC27", "SIOC28", "SIOC29", "SIOC30", "SIOC31",
163 "PSC0", "PSC1", "PSC2", "PSC3", "PSC4", "PSC5",
164 "AT0", "AT1", "AT2", "AT3", "AT4", "AT5", "AT6", "AT7",
165 "AT8", "AT9", "AT10", "AT11", "AT12", "AT13", "AT14", "AT15",
166 "NX0", "NX1", "NX2", "NX3", "NX4", "NX5", "NX6", "NX7",
167 "NX8", "NX9", "NX10", "NX11", "NX12", "NX13", "NX14", "NX15",
168 "J0", // minor is 186
169 "UL0","UL1","UL2","UL3",
170 "xvc0", // FAIL -- "/dev/xvc0" lacks "tty" prefix
171 "PZ0","PZ1","PZ2","PZ3",
172 "TX0","TX1","TX2","TX3","TX4","TX5","TX6","TX7",
173 "SC0","SC1","SC2","SC3",
174 "MAX0","MAX1","MAX2","MAX3",
180 #define AS(x) (sizeof(x)/sizeof((x)[0]))
181 int main(int argc, char *argv[]){
183 while(i<AS(low_density_names)){
184 printf("%3d = /dev/tty%.*s\n",i,sizeof low_density_names[i],low_density_names[i]);
191 /* Try to guess the device name (useful until /proc/PID/tty is added) */
192 static int guess_name(char *restrict const buf, unsigned maj, unsigned min){
195 unsigned tmpmin = min;
198 case 3: /* /dev/[pt]ty[p-za-o][0-9a-z] is 936 */
199 if(tmpmin > 255) return 0; // should never happen; array index protection
200 t0 = "pqrstuvwxyzabcde"[tmpmin>>4];
201 t1 = "0123456789abcdef"[tmpmin&0x0f];
202 sprintf(buf, "/dev/tty%c%c", t0, t1);
206 sprintf(buf, "/dev/tty%d", min);
209 sprintf(buf, "/dev/ttyS%d", min-64);
211 case 11: sprintf(buf, "/dev/ttyB%d", min); break;
212 case 17: sprintf(buf, "/dev/ttyH%d", min); break;
213 case 19: sprintf(buf, "/dev/ttyC%d", min); break;
214 case 22: sprintf(buf, "/dev/ttyD%d", min); break; /* devices.txt */
215 case 23: sprintf(buf, "/dev/ttyD%d", min); break; /* driver code */
216 case 24: sprintf(buf, "/dev/ttyE%d", min); break;
217 case 32: sprintf(buf, "/dev/ttyX%d", min); break;
218 case 43: sprintf(buf, "/dev/ttyI%d", min); break;
219 case 46: sprintf(buf, "/dev/ttyR%d", min); break;
220 case 48: sprintf(buf, "/dev/ttyL%d", min); break;
221 case 57: sprintf(buf, "/dev/ttyP%d", min); break;
222 case 71: sprintf(buf, "/dev/ttyF%d", min); break;
223 case 75: sprintf(buf, "/dev/ttyW%d", min); break;
224 case 78: sprintf(buf, "/dev/ttyM%d", min); break; /* conflict */
225 case 105: sprintf(buf, "/dev/ttyV%d", min); break;
226 case 112: sprintf(buf, "/dev/ttyM%d", min); break; /* conflict */
227 /* 136 ... 143 are /dev/pts/0, /dev/pts/1, /dev/pts/2 ... */
228 case 136 ... 143: sprintf(buf, "/dev/pts/%d", min+(maj-136)*256); break;
229 case 148: sprintf(buf, "/dev/ttyT%d", min); break;
230 case 154: sprintf(buf, "/dev/ttySR%d", min); break;
231 case 156: sprintf(buf, "/dev/ttySR%d", min+256); break;
232 case 164: sprintf(buf, "/dev/ttyCH%d", min); break;
233 case 166: sprintf(buf, "/dev/ttyACM%d", min); break; /* bummer, 9-char */
234 case 172: sprintf(buf, "/dev/ttyMX%d", min); break;
235 case 174: sprintf(buf, "/dev/ttySI%d", min); break;
236 case 188: sprintf(buf, "/dev/ttyUSB%d", min); break; /* bummer, 9-char */
238 if(min >= sizeof low_density_names / sizeof low_density_names[0]) return 0;
239 memcpy(buf,"/dev/tty",8);
240 memcpy(buf+8, low_density_names[min], sizeof low_density_names[0]);
241 buf[8 + sizeof low_density_names[0]] = '\0';
242 // snprintf(buf, 9 + sizeof low_density_names[0], "/dev/tty%.*s", sizeof low_density_names[0], low_density_names[min]);
244 case 208: sprintf(buf, "/dev/ttyU%d", min); break;
245 case 216: sprintf(buf, "/dev/ttyUB%d", min); break; // "/dev/rfcomm%d" now?
246 case 224: sprintf(buf, "/dev/ttyY%d", min); break;
247 case 227: sprintf(buf, "/dev/3270/tty%d", min); break; /* bummer, HUGE */
248 case 229: sprintf(buf, "/dev/iseries/vtty%d", min); break; /* bummer, HUGE */
249 case 256: sprintf(buf, "/dev/ttyEQ%d", min); break;
252 if(stat(buf, &sbuf) < 0) return 0;
253 if(min != MINOR_OF(sbuf.st_rdev)) return 0;
254 if(maj != MAJOR_OF(sbuf.st_rdev)) return 0;
258 /* Linux 2.2 can give us filenames that might be correct.
259 * Useful names could be in /proc/PID/fd/2 (stderr, seldom redirected)
260 * and in /proc/PID/fd/255 (used by bash to remember the tty).
262 static int link_name(char *restrict const buf, unsigned maj, unsigned min, int pid, const char *restrict name){
266 sprintf(path, "/proc/%d/%s", pid, name); /* often permission denied */
267 count = readlink(path,buf,TTY_NAME_SIZE-1);
268 if(count == -1) return 0;
270 if(stat(buf, &sbuf) < 0) return 0;
271 if(min != MINOR_OF(sbuf.st_rdev)) return 0;
272 if(maj != MAJOR_OF(sbuf.st_rdev)) return 0;
276 /* number --> name */
277 unsigned dev_to_tty(char *restrict ret, unsigned chop, dev_t dev_t_dev, int pid, unsigned int flags) {
278 static char buf[TTY_NAME_SIZE];
279 char *restrict tmp = buf;
280 unsigned dev = dev_t_dev;
283 if(dev == 0u) goto no_tty;
284 if(linux_version_code > LINUX_VERSION(2, 7, 0)){ // not likely to make 2.6.xx
285 if(link_name(tmp, MAJOR_OF(dev), MINOR_OF(dev), pid, "tty" )) goto abbrev;
287 if(driver_name(tmp, MAJOR_OF(dev), MINOR_OF(dev) )) goto abbrev;
288 if( link_name(tmp, MAJOR_OF(dev), MINOR_OF(dev), pid, "fd/2" )) goto abbrev;
289 if( guess_name(tmp, MAJOR_OF(dev), MINOR_OF(dev) )) goto abbrev;
290 if( link_name(tmp, MAJOR_OF(dev), MINOR_OF(dev), pid, "fd/255")) goto abbrev;
291 // fall through if unable to find a device file
296 if((flags&ABBREV_DEV) && !strncmp(tmp,"/dev/",5) && tmp[5]) tmp += 5;
297 if((flags&ABBREV_TTY) && !strncmp(tmp,"tty", 3) && tmp[3]) tmp += 3;
298 if((flags&ABBREV_PTS) && !strncmp(tmp,"pts/", 4) && tmp[4]) tmp += 4;
299 /* gotta check before we chop or we may chop someone else's memory */
300 if(chop + (unsigned long)(tmp-buf) <= sizeof buf)
302 /* replace non-ASCII characters with '?' and return the number of chars */
317 /* name --> number */
318 int tty_to_dev(const char *restrict const name) {
321 if(name[0]=='/' && stat(name, &sbuf) >= 0) return sbuf.st_rdev;
322 snprintf(buf,32,"/dev/%s",name);
323 if(stat(buf, &sbuf) >= 0) return sbuf.st_rdev;
324 snprintf(buf,32,"/dev/tty%s",name);
325 if(stat(buf, &sbuf) >= 0) return sbuf.st_rdev;
326 snprintf(buf,32,"/dev/pts/%s",name);
327 if(stat(buf, &sbuf) >= 0) return sbuf.st_rdev;