procps/proc/devname.c

285 lines
9.6 KiB
C
Raw Normal View History

2002-02-02 04:17:29 +05:30
/*
* Copyright 1998-2002 by Albert Cahalan; all rights resered.
2002-02-02 04:17:29 +05:30
* This file may be used subject to the terms and conditions of the
* GNU Library General Public License Version 2, or any later version
* at your option, as published by the Free Software Foundation.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "version.h"
2002-02-02 04:17:29 +05:30
#include "devname.h"
2004-07-15 10:59:56 +05:30
// This is the buffer size for a tty name. Any path is legal,
// which makes PAGE_SIZE appropriate (see kernel source), but
// that is only 99% portable and utmp only holds 32 anyway.
#define TTY_NAME_SIZE 128
2002-02-02 04:17:29 +05:30
/* Who uses what:
*
2003-01-15 16:22:39 +05:30
* tty_to_dev w (there is a fancy version in ps)
* dev_to_tty top, ps
2002-02-02 04:17:29 +05:30
*/
2003-09-28 08:15:05 +05:30
#if 0
#include <sys/sysmacros.h>
#define MAJOR_OF(d) ((unsigned)major(d))
#define MINOR_OF(d) ((unsigned)minor(d))
#else
#define MAJOR_OF(d) ( ((unsigned)(d)>>8u) & 0xfffu )
#define MINOR_OF(d) ( ((unsigned)(d)&0xffu) | (((unsigned)(d)&0xfff00000u)>>12u) )
2003-09-28 08:15:05 +05:30
#undef major
#undef minor
#define major <-- do not use -->
#define minor <-- do not use -->
#endif
2002-02-02 04:17:29 +05:30
typedef struct tty_map_node {
struct tty_map_node *next;
2003-09-28 08:15:05 +05:30
unsigned major_number;
unsigned minor_first, minor_last;
char name[16];
char devfs_type;
2002-02-02 04:17:29 +05:30
} tty_map_node;
static tty_map_node *tty_map = NULL;
/* Load /proc/tty/drivers for device name mapping use. */
static void load_drivers(void){
char buf[10000];
char *p;
int fd;
int bytes;
fd = open("/proc/tty/drivers",O_RDONLY);
if(fd == -1) goto fail;
bytes = read(fd, buf, sizeof(buf) - 1);
2002-02-02 04:17:29 +05:30
if(bytes == -1) goto fail;
buf[bytes] = '\0';
p = buf;
while(( p = strstr(p, " /dev/") )){
2002-02-02 04:17:29 +05:30
tty_map_node *tmn;
int len;
char *end;
p += 6;
end = strchr(p, ' ');
if(!end) continue;
len = end - p;
tmn = calloc(1, sizeof(tty_map_node));
2002-02-02 04:17:29 +05:30
tmn->next = tty_map;
tty_map = tmn;
/* if we have a devfs type name such as /dev/tts/%d then strip the %d but
keep a flag. */
if(len >= 3 && !strncmp(end - 2, "%d", 2)){
len -= 2;
tmn->devfs_type = 1;
}
2002-02-02 04:17:29 +05:30
strncpy(tmn->name, p, len);
p = end; /* set p to point past the %d as well if there is one */
while(*p == ' ') p++;
2002-02-02 04:17:29 +05:30
tmn->major_number = atoi(p);
p += strspn(p, "0123456789");
while(*p == ' ') p++;
2003-09-28 08:15:05 +05:30
switch(sscanf(p, "%u-%u", &tmn->minor_first, &tmn->minor_last)){
default:
/* Can't finish parsing this line so we remove it from the list */
tty_map = tty_map->next;
free(tmn);
break;
case 1:
tmn->minor_last = tmn->minor_first;
break;
case 2:
break;
}
2002-02-02 04:17:29 +05:30
}
fail:
if(fd != -1) close(fd);
if(!tty_map) tty_map = (tty_map_node *)-1;
}
/* Try to guess the device name from /proc/tty/drivers info. */
2003-09-28 08:15:05 +05:30
static int driver_name(char *restrict const buf, unsigned maj, unsigned min){
2002-02-02 04:17:29 +05:30
struct stat sbuf;
tty_map_node *tmn;
if(!tty_map) load_drivers();
if(tty_map == (tty_map_node *)-1) return 0;
tmn = tty_map;
for(;;){
if(!tmn) return 0;
if(tmn->major_number == maj && tmn->minor_first <= min && tmn->minor_last >= min) break;
2002-02-02 04:17:29 +05:30
tmn = tmn->next;
}
sprintf(buf, "/dev/%s%d", tmn->name, min); /* like "/dev/ttyZZ255" */
if(stat(buf, &sbuf) < 0){
if(tmn->devfs_type) return 0;
sprintf(buf, "/dev/%s", tmn->name); /* like "/dev/ttyZZ255" */
if(stat(buf, &sbuf) < 0) return 0;
}
2003-09-28 08:15:05 +05:30
if(min != MINOR_OF(sbuf.st_rdev)) return 0;
if(maj != MAJOR_OF(sbuf.st_rdev)) return 0;
2002-02-02 04:17:29 +05:30
return 1;
}
2004-02-25 04:09:48 +05:30
// major 204 is a mess -- "Low-density serial ports"
static const char low_density_names[][4] = {
"LU0", "LU1", "LU2", "LU3",
"FB0",
"SA0", "SA1", "SA2",
"SC0", "SC1", "SC2", "SC3",
"FW0", "FW1", "FW2", "FW3",
"AM0", "AM1", "AM2", "AM3", "AM4", "AM5", "AM6", "AM7",
"AM8", "AM9", "AM10", "AM11", "AM12", "AM13", "AM14", "AM15",
"DB0", "DB1", "DB2", "DB3", "DB4", "DB5", "DB6", "DB7",
2004-07-17 08:45:21 +05:30
"SG0",
"SMX0", "SMX1", "SMX2",
"MM0", "MM1",
2004-02-25 04:09:48 +05:30
};
2002-02-02 04:17:29 +05:30
/* Try to guess the device name (useful until /proc/PID/tty is added) */
2003-09-28 08:15:05 +05:30
static int guess_name(char *restrict const buf, unsigned maj, unsigned min){
2002-02-02 04:17:29 +05:30
struct stat sbuf;
int t0, t1;
2003-09-28 08:15:05 +05:30
unsigned tmpmin = min;
2004-02-25 04:09:48 +05:30
2002-02-02 04:17:29 +05:30
switch(maj){
case 4:
if(min<64){
sprintf(buf, "/dev/tty%d", min);
break;
}
if(min<128){ /* to 255 on newer systems */
sprintf(buf, "/dev/ttyS%d", min-64);
break;
}
tmpmin = min & 0x3f; /* FALL THROUGH */
case 3: /* /dev/[pt]ty[p-za-o][0-9a-z] is 936 */
if(tmpmin > 255) return 0; // should never happen; array index protection
2002-02-02 04:17:29 +05:30
t0 = "pqrstuvwxyzabcde"[tmpmin>>4];
t1 = "0123456789abcdef"[tmpmin&0x0f];
sprintf(buf, "/dev/tty%c%c", t0, t1);
break;
2004-03-18 00:00:10 +05:30
case 11: sprintf(buf, "/dev/ttyB%d", min); break;
2002-02-02 04:17:29 +05:30
case 17: sprintf(buf, "/dev/ttyH%d", min); break;
case 19: sprintf(buf, "/dev/ttyC%d", min); break;
case 22: sprintf(buf, "/dev/ttyD%d", min); break; /* devices.txt */
case 23: sprintf(buf, "/dev/ttyD%d", min); break; /* driver code */
case 24: sprintf(buf, "/dev/ttyE%d", min); break;
case 32: sprintf(buf, "/dev/ttyX%d", min); break;
case 43: sprintf(buf, "/dev/ttyI%d", min); break;
case 46: sprintf(buf, "/dev/ttyR%d", min); break;
case 48: sprintf(buf, "/dev/ttyL%d", min); break;
case 57: sprintf(buf, "/dev/ttyP%d", min); break;
case 71: sprintf(buf, "/dev/ttyF%d", min); break;
case 75: sprintf(buf, "/dev/ttyW%d", min); break;
case 78: sprintf(buf, "/dev/ttyM%d", min); break; /* conflict */
case 105: sprintf(buf, "/dev/ttyV%d", min); break;
case 112: sprintf(buf, "/dev/ttyM%d", min); break; /* conflict */
/* 136 ... 143 are /dev/pts/0, /dev/pts/1, /dev/pts/2 ... */
case 136 ... 143: sprintf(buf, "/dev/pts/%d", min+(maj-136)*256); break;
case 148: sprintf(buf, "/dev/ttyT%d", min); break;
case 154: sprintf(buf, "/dev/ttySR%d", min); break;
case 156: sprintf(buf, "/dev/ttySR%d", min+256); break;
case 164: sprintf(buf, "/dev/ttyCH%d", min); break;
case 166: sprintf(buf, "/dev/ttyACM%d", min); break; /* bummer, 9-char */
case 172: sprintf(buf, "/dev/ttyMX%d", min); break;
case 174: sprintf(buf, "/dev/ttySI%d", min); break;
case 188: sprintf(buf, "/dev/ttyUSB%d", min); break; /* bummer, 9-char */
2004-02-25 04:09:48 +05:30
case 204:
if(min >= sizeof low_density_names / sizeof low_density_names[0]) return 0;
sprintf(buf, "/dev/tty%s", low_density_names[min]);
break;
case 208: sprintf(buf, "/dev/ttyU%d", min); break;
case 216: sprintf(buf, "/dev/ttyUB%d", min); break;
case 224: sprintf(buf, "/dev/ttyY%d", min); break;
case 227: sprintf(buf, "/dev/3270/tty%d", min); break; /* bummer, HUGE */
case 229: sprintf(buf, "/dev/iseries/vtty%d", min); break; /* bummer, HUGE */
2002-02-02 04:17:29 +05:30
default: return 0;
}
if(stat(buf, &sbuf) < 0) return 0;
2003-09-28 08:15:05 +05:30
if(min != MINOR_OF(sbuf.st_rdev)) return 0;
if(maj != MAJOR_OF(sbuf.st_rdev)) return 0;
2002-02-02 04:17:29 +05:30
return 1;
}
/* Linux 2.2 can give us filenames that might be correct.
* Useful names could be in /proc/PID/fd/2 (stderr, seldom redirected)
* and in /proc/PID/fd/255 (used by bash to remember the tty).
*/
2003-09-28 08:15:05 +05:30
static int link_name(char *restrict const buf, unsigned maj, unsigned min, int pid, const char *restrict name){
2002-02-02 04:17:29 +05:30
struct stat sbuf;
char path[32];
int count;
sprintf(path, "/proc/%d/%s", pid, name); /* often permission denied */
count = readlink(path,buf,TTY_NAME_SIZE-1);
2002-02-02 04:17:29 +05:30
if(count == -1) return 0;
buf[count] = '\0';
if(stat(buf, &sbuf) < 0) return 0;
2003-09-28 08:15:05 +05:30
if(min != MINOR_OF(sbuf.st_rdev)) return 0;
if(maj != MAJOR_OF(sbuf.st_rdev)) return 0;
2002-02-02 04:17:29 +05:30
return 1;
}
/* number --> name */
2003-09-28 08:15:05 +05:30
unsigned dev_to_tty(char *restrict ret, unsigned chop, dev_t dev_t_dev, int pid, unsigned int flags) {
static char buf[TTY_NAME_SIZE];
2002-11-25 15:46:33 +05:30
char *restrict tmp = buf;
2003-09-28 08:15:05 +05:30
unsigned dev = dev_t_dev;
2002-11-25 15:46:33 +05:30
unsigned i = 0;
2002-02-02 04:17:29 +05:30
int c;
2003-09-28 08:15:05 +05:30
if(dev == 0u) goto no_tty;
2002-12-04 07:29:45 +05:30
if(linux_version_code > LINUX_VERSION(2, 7, 0)){ // not likely to make 2.6.xx
2003-09-28 08:15:05 +05:30
if(link_name(tmp, MAJOR_OF(dev), MINOR_OF(dev), pid, "tty" )) goto abbrev;
}
2003-09-28 08:15:05 +05:30
if(driver_name(tmp, MAJOR_OF(dev), MINOR_OF(dev) )) goto abbrev;
if( link_name(tmp, MAJOR_OF(dev), MINOR_OF(dev), pid, "fd/2" )) goto abbrev;
if( guess_name(tmp, MAJOR_OF(dev), MINOR_OF(dev) )) goto abbrev;
if( link_name(tmp, MAJOR_OF(dev), MINOR_OF(dev), pid, "fd/255")) goto abbrev;
2002-12-03 14:37:59 +05:30
// fall through if unable to find a device file
no_tty:
2002-02-02 04:17:29 +05:30
strcpy(ret, "?");
return 1;
abbrev:
if((flags&ABBREV_DEV) && !strncmp(tmp,"/dev/",5) && tmp[5]) tmp += 5;
if((flags&ABBREV_TTY) && !strncmp(tmp,"tty", 3) && tmp[3]) tmp += 3;
if((flags&ABBREV_PTS) && !strncmp(tmp,"pts/", 4) && tmp[4]) tmp += 4;
/* gotta check before we chop or we may chop someone else's memory */
2002-11-25 15:46:33 +05:30
if(chop + (unsigned long)(tmp-buf) <= sizeof buf)
tmp[chop] = '\0';
/* replace non-ASCII characters with '?' and return the number of chars */
2002-02-02 04:17:29 +05:30
for(;;){
c = *tmp;
tmp++;
if(!c) break;
i++;
if(c<=' ') c = '?';
if(c>126) c = '?';
*ret = c;
ret++;
}
*ret = '\0';
return i;
}
/* name --> number */
2002-11-25 15:46:33 +05:30
int tty_to_dev(const char *restrict const name) {
2002-02-02 04:17:29 +05:30
struct stat sbuf;
static char buf[32];
2002-12-03 14:37:59 +05:30
if(name[0]=='/' && stat(name, &sbuf) >= 0) return sbuf.st_rdev;
2002-02-02 04:17:29 +05:30
snprintf(buf,32,"/dev/%s",name);
if(stat(buf, &sbuf) >= 0) return sbuf.st_rdev;
snprintf(buf,32,"/dev/tty%s",name);
if(stat(buf, &sbuf) >= 0) return sbuf.st_rdev;
snprintf(buf,32,"/dev/pts/%s",name);
if(stat(buf, &sbuf) >= 0) return sbuf.st_rdev;
return -1;
}