Blame compat/dev_to_tty.c

Packit Service 569379
#ifdef HAVE_CONFIG_H
Packit Service 569379
#include <config.h>
Packit Service 569379
#endif
Packit Service 569379
Packit Service 569379
Packit Service 569379
/*
Packit Service 569379
 * devname - device name functions
Packit Service 569379
 * Copyright 1998-2002 by Albert Cahalan
Packit Service 569379
 *
Packit Service 569379
 * This library is free software; you can redistribute it and/or
Packit Service 569379
 * modify it under the terms of the GNU Lesser General Public
Packit Service 569379
 * License as published by the Free Software Foundation; either
Packit Service 569379
 * version 2.1 of the License, or (at your option) any later version.
Packit Service 569379
 *
Packit Service 569379
 * This library is distributed in the hope that it will be useful,
Packit Service 569379
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 569379
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service 569379
 * Lesser General Public License for more details.
Packit Service 569379
 *
Packit Service 569379
 * You should have received a copy of the GNU Lesser General Public
Packit Service 569379
 * License along with this library; if not, write to the Free Software
Packit Service 569379
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
Packit Service 569379
 */
Packit Service 569379
Packit Service 569379
#include <stdio.h>
Packit Service 569379
#include <stdlib.h>
Packit Service 569379
#include <string.h>
Packit Service 569379
#include <sys/types.h>
Packit Service 569379
#include <sys/stat.h>
Packit Service 569379
#include <fcntl.h>
Packit Service 569379
#include <unistd.h>
Packit Service 569379
Packit Service 569379
#include "compat.h"
Packit Service 569379
Packit Service 569379
// This is the buffer size for a tty name. Any path is legal,
Packit Service 569379
// which makes PAGE_SIZE appropriate (see kernel source), but
Packit Service 569379
// that is only 99% portable and utmp only holds 32 anyway.
Packit Service 569379
// We need at least 20 for guess_name().
Packit Service 569379
#define TTY_NAME_SIZE 128
Packit Service 569379
Packit Service 569379
Packit Service 569379
#if 0
Packit Service 569379
#include <sys/sysmacros.h>
Packit Service 569379
#define MAJOR_OF(d) ((unsigned)major(d))
Packit Service 569379
#define MINOR_OF(d) ((unsigned)minor(d))
Packit Service 569379
#else
Packit Service 569379
#define MAJOR_OF(d) ( ((unsigned)(d)>>8u) & 0xfffu )
Packit Service 569379
#define MINOR_OF(d) ( ((unsigned)(d)&0xffu) | (((unsigned)(d)&0xfff00000u)>>12u) )
Packit Service 569379
#undef major
Packit Service 569379
#undef minor
Packit Service 569379
#define major <-- do not use -->
Packit Service 569379
#define minor <-- do not use -->
Packit Service 569379
#endif
Packit Service 569379
Packit Service 569379
typedef struct tty_map_node {
Packit Service 569379
  struct tty_map_node *next;
Packit Service 569379
  unsigned short devfs_type;  // bool
Packit Service 569379
  unsigned short major_number;
Packit Service 569379
  unsigned minor_first;
Packit Service 569379
  unsigned minor_last;
Packit Service 569379
  char name[16];
Packit Service 569379
} tty_map_node;
Packit Service 569379
Packit Service 569379
static tty_map_node *tty_map = NULL;
Packit Service 569379
Packit Service 569379
/* Load /proc/tty/drivers for device name mapping use. */
Packit Service 569379
static void load_drivers(void){
Packit Service 569379
  char buf[10000];
Packit Service 569379
  char *p;
Packit Service 569379
  int fd;
Packit Service 569379
  int bytes;
Packit Service 569379
  fd = open("/proc/tty/drivers",O_RDONLY);
Packit Service 569379
  if(fd == -1) goto fail;
Packit Service 569379
  bytes = read(fd, buf, sizeof(buf) - 1);
Packit Service 569379
  if(bytes == -1) goto fail;
Packit Service 569379
  buf[bytes] = '\0';
Packit Service 569379
  p = buf;
Packit Service 569379
  while(( p = strstr(p, " /dev/") )){  // " /dev/" is the second column
Packit Service 569379
    tty_map_node *tmn;
Packit Service 569379
    size_t len;
Packit Service 569379
    char *end;
Packit Service 569379
    p += 6;
Packit Service 569379
    end = strchr(p, ' ');
Packit Service 569379
    if(!end) continue;
Packit Service 569379
    len = end - p;
Packit Service 569379
    tmn = malloc(sizeof(tty_map_node));
Packit Service 569379
    tmn->next = tty_map;
Packit Service 569379
    tty_map = tmn;
Packit Service 569379
    /* if we have a devfs type name such as /dev/tts/%d then strip the %d but
Packit Service 569379
       keep a flag. */
Packit Service 569379
    if(len >= 3 && !strncmp(end - 2, "%d", 2)){
Packit Service 569379
      len -= 2;
Packit Service 569379
      tmn->devfs_type = 1;
Packit Service 569379
    }
Packit Service 569379
    if(len >= sizeof tmn->name)
Packit Service 569379
      len = sizeof tmn->name - 1; // mangle it to avoid overflow
Packit Service 569379
    memcpy(tmn->name, p, len);
Packit Service 569379
    p = end; /* set p to point past the %d as well if there is one */
Packit Service 569379
    while(*p == ' ') p++;
Packit Service 569379
    tmn->major_number = atoi(p);
Packit Service 569379
    p += strspn(p, "0123456789");
Packit Service 569379
    while(*p == ' ') p++;
Packit Service 569379
    switch(sscanf(p, "%u-%u", &tmn->minor_first, &tmn->minor_last)){
Packit Service 569379
    default:
Packit Service 569379
      /* Can't finish parsing this line so we remove it from the list */
Packit Service 569379
      tty_map = tty_map->next;
Packit Service 569379
      free(tmn);
Packit Service 569379
      break;
Packit Service 569379
    case 1:
Packit Service 569379
      tmn->minor_last = tmn->minor_first;
Packit Service 569379
      break;
Packit Service 569379
    case 2:
Packit Service 569379
      break;
Packit Service 569379
    }
Packit Service 569379
  }
Packit Service 569379
fail:
Packit Service 569379
  if(fd != -1) close(fd);
Packit Service 569379
  if(!tty_map) tty_map = (tty_map_node *)-1;
Packit Service 569379
}
Packit Service 569379
Packit Service 569379
/* Try to guess the device name from /proc/tty/drivers info. */
Packit Service 569379
static int driver_name(char *restrict const buf, unsigned maj, unsigned min){
Packit Service 569379
  struct stat sbuf;
Packit Service 569379
  tty_map_node *tmn;
Packit Service 569379
  if(!tty_map) load_drivers();
Packit Service 569379
  if(tty_map == (tty_map_node *)-1) return 0;
Packit Service 569379
  tmn = tty_map;
Packit Service 569379
  for(;;){
Packit Service 569379
    if(!tmn) return 0;
Packit Service 569379
    if(tmn->major_number == maj && tmn->minor_first <= min && tmn->minor_last >= min) break;
Packit Service 569379
    tmn = tmn->next;
Packit Service 569379
  }
Packit Service 569379
  sprintf(buf, "/dev/%s%d", tmn->name, min);  /* like "/dev/ttyZZ255" */
Packit Service 569379
  if(stat(buf, &sbuf) < 0){
Packit Service 569379
    if(tmn->devfs_type) return 0;
Packit Service 569379
    sprintf(buf, "/dev/%s", tmn->name);  /* like "/dev/ttyZZ255" */
Packit Service 569379
    if(stat(buf, &sbuf) < 0) return 0;
Packit Service 569379
  }
Packit Service 569379
  if(min != MINOR_OF(sbuf.st_rdev)) return 0;
Packit Service 569379
  if(maj != MAJOR_OF(sbuf.st_rdev)) return 0;
Packit Service 569379
  return 1;
Packit Service 569379
}
Packit Service 569379
Packit Service 569379
// major 204 is a mess -- "Low-density serial ports"
Packit Service 569379
static const char low_density_names[][6] = {
Packit Service 569379
"LU0",  "LU1",  "LU2",  "LU3",
Packit Service 569379
"FB0",
Packit Service 569379
"SA0",  "SA1",  "SA2",
Packit Service 569379
"SC0",  "SC1",  "SC2",  "SC3",
Packit Service 569379
"FW0",  "FW1",  "FW2",  "FW3",
Packit Service 569379
"AM0",  "AM1",  "AM2",  "AM3",  "AM4",  "AM5",  "AM6",  "AM7",
Packit Service 569379
"AM8",  "AM9",  "AM10", "AM11", "AM12", "AM13", "AM14", "AM15",
Packit Service 569379
"DB0",  "DB1",  "DB2",  "DB3",  "DB4",  "DB5",  "DB6",  "DB7",
Packit Service 569379
"SG0",
Packit Service 569379
"SMX0",  "SMX1",  "SMX2",
Packit Service 569379
"MM0",  "MM1",
Packit Service 569379
"CPM0", "CPM1", "CPM2", "CPM3", /* "CPM4", "CPM5", */  // bad allocation?
Packit Service 569379
"IOC0",  "IOC1",  "IOC2",  "IOC3",  "IOC4",  "IOC5",  "IOC6",  "IOC7",
Packit Service 569379
"IOC8",  "IOC9",  "IOC10", "IOC11", "IOC12", "IOC13", "IOC14", "IOC15",
Packit Service 569379
"IOC16", "IOC17", "IOC18", "IOC19", "IOC20", "IOC21", "IOC22", "IOC23",
Packit Service 569379
"IOC24", "IOC25", "IOC26", "IOC27", "IOC28", "IOC29", "IOC30", "IOC31",
Packit Service 569379
"VR0", "VR1",
Packit Service 569379
"IOC84",  "IOC85",  "IOC86",  "IOC87",  "IOC88",  "IOC89",  "IOC90",  "IOC91",
Packit Service 569379
"IOC92",  "IOC93",  "IOC94", "IOC95", "IOC96", "IOC97", "IOC98", "IOC99",
Packit Service 569379
"IOC100", "IOC101", "IOC102", "IOC103", "IOC104", "IOC105", "IOC106", "IOC107",
Packit Service 569379
"IOC108", "IOC109", "IOC110", "IOC111", "IOC112", "IOC113", "IOC114", "IOC115",
Packit Service 569379
"SIOC0",  "SIOC1",  "SIOC2",  "SIOC3",  "SIOC4",  "SIOC5",  "SIOC6",  "SIOC7",
Packit Service 569379
"SIOC8",  "SIOC9",  "SIOC10", "SIOC11", "SIOC12", "SIOC13", "SIOC14", "SIOC15",
Packit Service 569379
"SIOC16", "SIOC17", "SIOC18", "SIOC19", "SIOC20", "SIOC21", "SIOC22", "SIOC23",
Packit Service 569379
"SIOC24", "SIOC25", "SIOC26", "SIOC27", "SIOC28", "SIOC29", "SIOC30", "SIOC31",
Packit Service 569379
"PSC0", "PSC1", "PSC2", "PSC3", "PSC4", "PSC5",
Packit Service 569379
"AT0",  "AT1",  "AT2",  "AT3",  "AT4",  "AT5",  "AT6",  "AT7",
Packit Service 569379
"AT8",  "AT9",  "AT10", "AT11", "AT12", "AT13", "AT14", "AT15",
Packit Service 569379
"NX0",  "NX1",  "NX2",  "NX3",  "NX4",  "NX5",  "NX6",  "NX7",
Packit Service 569379
"NX8",  "NX9",  "NX10", "NX11", "NX12", "NX13", "NX14", "NX15",
Packit Service 569379
"J0",   // minor is 186
Packit Service 569379
"UL0","UL1","UL2","UL3",
Packit Service 569379
"xvc0", // FAIL -- "/dev/xvc0" lacks "tty" prefix
Packit Service 569379
"PZ0","PZ1","PZ2","PZ3",
Packit Service 569379
"TX0","TX1","TX2","TX3","TX4","TX5","TX6","TX7",
Packit Service 569379
"SC0","SC1","SC2","SC3",
Packit Service 569379
"MAX0","MAX1","MAX2","MAX3",
Packit Service 569379
};
Packit Service 569379
Packit Service 569379
#if 0
Packit Service 569379
// test code
Packit Service 569379
#include <stdio.h>
Packit Service 569379
#define AS(x) (sizeof(x)/sizeof((x)[0]))
Packit Service 569379
int main(int argc, char *argv[]){
Packit Service 569379
  int i = 0;
Packit Service 569379
  while(i
Packit Service 569379
    printf("%3d = /dev/tty%.*s\n",i,sizeof low_density_names[i],low_density_names[i]);
Packit Service 569379
    i++;
Packit Service 569379
  }
Packit Service 569379
  return 0;
Packit Service 569379
}
Packit Service 569379
#endif
Packit Service 569379
Packit Service 569379
/* Try to guess the device name (useful until /proc/PID/tty is added) */
Packit Service 569379
static int guess_name(char *restrict const buf, unsigned maj, unsigned min){
Packit Service 569379
  struct stat sbuf;
Packit Service 569379
  int t0, t1;
Packit Service 569379
  unsigned tmpmin = min;
Packit Service 569379
Packit Service 569379
  switch(maj){
Packit Service 569379
  case   3:      /* /dev/[pt]ty[p-za-o][0-9a-z] is 936 */
Packit Service 569379
    if(tmpmin > 255) return 0;   // should never happen; array index protection
Packit Service 569379
    t0 = "pqrstuvwxyzabcde"[tmpmin>>4];
Packit Service 569379
    t1 = "0123456789abcdef"[tmpmin&0x0f];
Packit Service 569379
    sprintf(buf, "/dev/tty%c%c", t0, t1);
Packit Service 569379
    break;
Packit Service 569379
  case   4:
Packit Service 569379
    if(min<64){
Packit Service 569379
      sprintf(buf, "/dev/tty%d", min);
Packit Service 569379
      break;
Packit Service 569379
    }
Packit Service 569379
    sprintf(buf, "/dev/ttyS%d", min-64);
Packit Service 569379
    break;
Packit Service 569379
  case  11:  sprintf(buf, "/dev/ttyB%d",  min); break;
Packit Service 569379
  case  17:  sprintf(buf, "/dev/ttyH%d",  min); break;
Packit Service 569379
  case  19:  sprintf(buf, "/dev/ttyC%d",  min); break;
Packit Service 569379
  case  22:  sprintf(buf, "/dev/ttyD%d",  min); break; /* devices.txt */
Packit Service 569379
  case  23:  sprintf(buf, "/dev/ttyD%d",  min); break; /* driver code */
Packit Service 569379
  case  24:  sprintf(buf, "/dev/ttyE%d",  min); break;
Packit Service 569379
  case  32:  sprintf(buf, "/dev/ttyX%d",  min); break;
Packit Service 569379
  case  43:  sprintf(buf, "/dev/ttyI%d",  min); break;
Packit Service 569379
  case  46:  sprintf(buf, "/dev/ttyR%d",  min); break;
Packit Service 569379
  case  48:  sprintf(buf, "/dev/ttyL%d",  min); break;
Packit Service 569379
  case  57:  sprintf(buf, "/dev/ttyP%d",  min); break;
Packit Service 569379
  case  71:  sprintf(buf, "/dev/ttyF%d",  min); break;
Packit Service 569379
  case  75:  sprintf(buf, "/dev/ttyW%d",  min); break;
Packit Service 569379
  case  78:  sprintf(buf, "/dev/ttyM%d",  min); break; /* conflict */
Packit Service 569379
  case 105:  sprintf(buf, "/dev/ttyV%d",  min); break;
Packit Service 569379
  case 112:  sprintf(buf, "/dev/ttyM%d",  min); break; /* conflict */
Packit Service 569379
  /* 136 ... 143 are /dev/pts/0, /dev/pts/1, /dev/pts/2 ... */
Packit Service 569379
  case 136 ... 143:  sprintf(buf, "/dev/pts/%d",  min+(maj-136)*256); break;
Packit Service 569379
  case 148:  sprintf(buf, "/dev/ttyT%d",  min); break;
Packit Service 569379
  case 154:  sprintf(buf, "/dev/ttySR%d", min); break;
Packit Service 569379
  case 156:  sprintf(buf, "/dev/ttySR%d", min+256); break;
Packit Service 569379
  case 164:  sprintf(buf, "/dev/ttyCH%d",  min); break;
Packit Service 569379
  case 166:  sprintf(buf, "/dev/ttyACM%d", min); break; /* bummer, 9-char */
Packit Service 569379
  case 172:  sprintf(buf, "/dev/ttyMX%d",  min); break;
Packit Service 569379
  case 174:  sprintf(buf, "/dev/ttySI%d",  min); break;
Packit Service 569379
  case 188:  sprintf(buf, "/dev/ttyUSB%d", min); break; /* bummer, 9-char */
Packit Service 569379
  case 204:
Packit Service 569379
    if(min >= sizeof low_density_names / sizeof low_density_names[0]) return 0;
Packit Service 569379
    memcpy(buf,"/dev/tty",8);
Packit Service 569379
    memcpy(buf+8, low_density_names[min], sizeof low_density_names[0]);
Packit Service 569379
    buf[8 + sizeof low_density_names[0]] = '\0';
Packit Service 569379
//    snprintf(buf, 9 + sizeof low_density_names[0], "/dev/tty%.*s", sizeof low_density_names[0], low_density_names[min]);
Packit Service 569379
    break;
Packit Service 569379
  case 208:  sprintf(buf, "/dev/ttyU%d",  min); break;
Packit Service 569379
  case 216:  sprintf(buf, "/dev/ttyUB%d",  min); break; // "/dev/rfcomm%d" now?
Packit Service 569379
  case 224:  sprintf(buf, "/dev/ttyY%d",  min); break;
Packit Service 569379
  case 227:  sprintf(buf, "/dev/3270/tty%d", min); break; /* bummer, HUGE */
Packit Service 569379
  case 229:  sprintf(buf, "/dev/iseries/vtty%d",  min); break; /* bummer, HUGE */
Packit Service 569379
  case 256:  sprintf(buf, "/dev/ttyEQ%d",  min); break;
Packit Service 569379
  default: return 0;
Packit Service 569379
  }
Packit Service 569379
  if(stat(buf, &sbuf) < 0) return 0;
Packit Service 569379
  if(min != MINOR_OF(sbuf.st_rdev)) return 0;
Packit Service 569379
  if(maj != MAJOR_OF(sbuf.st_rdev)) return 0;
Packit Service 569379
  return 1;
Packit Service 569379
}
Packit Service 569379
Packit Service 569379
/* Linux 2.2 can give us filenames that might be correct.
Packit Service 569379
 * Useful names could be in /proc/PID/fd/2 (stderr, seldom redirected)
Packit Service 569379
 * and in /proc/PID/fd/255 (used by bash to remember the tty).
Packit Service 569379
 */
Packit Service 569379
static int link_name(char *restrict const buf, unsigned maj, unsigned min, int pid, const char *restrict name){
Packit Service 569379
  struct stat sbuf;
Packit Service 569379
  char path[32];
Packit Service 569379
  int count;
Packit Service 569379
  sprintf(path, "/proc/%d/%s", pid, name);  /* often permission denied */
Packit Service 569379
  count = readlink(path,buf,TTY_NAME_SIZE-1);
Packit Service 569379
  if(count == -1) return 0;
Packit Service 569379
  buf[count] = '\0';
Packit Service 569379
  if(stat(buf, &sbuf) < 0) return 0;
Packit Service 569379
  if(min != MINOR_OF(sbuf.st_rdev)) return 0;
Packit Service 569379
  if(maj != MAJOR_OF(sbuf.st_rdev)) return 0;
Packit Service 569379
  return 1;
Packit Service 569379
}
Packit Service 569379
Packit Service 569379
/* number --> name */
Packit Service 569379
unsigned dev_to_tty(char *restrict ret, unsigned chop, dev_t dev_t_dev, int pid, unsigned int flags) {
Packit Service 569379
  static char buf[TTY_NAME_SIZE];
Packit Service 569379
  char *restrict tmp = buf;
Packit Service 569379
  unsigned dev = dev_t_dev;
Packit Service 569379
  unsigned i = 0;
Packit Service 569379
  int c;
Packit Service 569379
  if(dev == 0u) goto no_tty;
Packit Service 569379
  if(driver_name(tmp, MAJOR_OF(dev), MINOR_OF(dev)               )) goto abbrev;
Packit Service 569379
  if(  link_name(tmp, MAJOR_OF(dev), MINOR_OF(dev), pid, "fd/2"  )) goto abbrev;
Packit Service 569379
  if( guess_name(tmp, MAJOR_OF(dev), MINOR_OF(dev)               )) goto abbrev;
Packit Service 569379
  if(  link_name(tmp, MAJOR_OF(dev), MINOR_OF(dev), pid, "fd/255")) goto abbrev;
Packit Service 569379
  // fall through if unable to find a device file
Packit Service 569379
no_tty:
Packit Service 569379
  strcpy(ret, "?");
Packit Service 569379
  return 1;
Packit Service 569379
abbrev:
Packit Service 569379
  if((flags&ABBREV_DEV) && !strncmp(tmp,"/dev/",5) && tmp[5]) tmp += 5;
Packit Service 569379
  if((flags&ABBREV_TTY) && !strncmp(tmp,"tty",  3) && tmp[3]) tmp += 3;
Packit Service 569379
  if((flags&ABBREV_PTS) && !strncmp(tmp,"pts/", 4) && tmp[4]) tmp += 4;
Packit Service 569379
  /* gotta check before we chop or we may chop someone else's memory */
Packit Service 569379
  if(chop + (unsigned long)(tmp-buf) <= sizeof buf)
Packit Service 569379
    tmp[chop] = '\0';
Packit Service 569379
  /* replace non-ASCII characters with '?' and return the number of chars */
Packit Service 569379
  for(;;){
Packit Service 569379
    c = *tmp;
Packit Service 569379
    tmp++;
Packit Service 569379
    if(!c) break;
Packit Service 569379
    i++;
Packit Service 569379
    if(c<=' ') c = '?';
Packit Service 569379
    if(c>126)  c = '?';
Packit Service 569379
    *ret = c;
Packit Service 569379
    ret++;
Packit Service 569379
  }
Packit Service 569379
  *ret = '\0';
Packit Service 569379
  return i;
Packit Service 569379
}