toybox/toys/pending/getty.c
<<
>>
Prefs
   1/* getty.c - A getty program to get controlling terminal.
   2 *
   3 * Copyright 2012 Sandeep Sharma <sandeep.jack2756@gamil.com>
   4 * Copyright 2013 Kyungwan Han <asura321@gmail.com>
   5 *
   6 * No Standard.
   7
   8USE_GETTY(NEWTOY(getty, "<2t#<0H:I:l:f:iwnmLh",TOYFLAG_SBIN))
   9
  10config GETTY
  11  bool "getty"
  12  default n
  13  help
  14    usage: getty [OPTIONS] BAUD_RATE[,BAUD_RATE]... TTY [TERMTYPE]
  15
  16    -h    Enable hardware RTS/CTS flow control
  17    -L    Set CLOCAL (ignore Carrier Detect state)
  18    -m    Get baud rate from modem's CONNECT status message
  19    -n    Don't prompt for login name
  20    -w    Wait for CR or LF before sending /etc/issue
  21    -i    Don't display /etc/issue
  22    -f ISSUE_FILE  Display ISSUE_FILE instead of /etc/issue
  23    -l LOGIN  Invoke LOGIN instead of /bin/login
  24    -t SEC    Terminate after SEC if no login name is read
  25    -I INITSTR  Send INITSTR before anything else
  26    -H HOST    Log HOST into the utmp file as the hostname
  27*/
  28#define FOR_getty
  29#include "toys.h"
  30#include <utmp.h>
  31
  32GLOBALS(
  33  char *issue_str;
  34  char *login_str;
  35  char *init_str;
  36  char *host_str; 
  37  long timeout;
  38  
  39  char *tty_name;  
  40  int  speeds[20];
  41  int  sc;              
  42  struct termios termios;
  43  char buff[128];
  44)
  45
  46#define CTL(x)        ((x) ^ 0100) 
  47#define HOSTNAME_SIZE 32
  48
  49typedef void (*sighandler_t)(int);
  50struct speed_mapper {
  51  long speed;
  52  speed_t code;
  53};
  54
  55struct speed_mapper speedtab[] = {
  56  {50, B50}, {75, B75}, {110, B110}, {134, B134}, {150, B150}, {200, B200},
  57  {300, B300}, {600, B600}, {1200, B1200}, {1800, B1800}, {2400, B2400},
  58  {4800, B4800}, {9600, B9600},
  59#ifdef  B19200
  60  {19200, B19200},
  61#endif
  62#ifdef  B38400
  63  {38400, B38400},
  64#endif
  65#ifdef  EXTA
  66  {19200, EXTA},
  67#endif
  68#ifdef  EXTB
  69  {38400, B38400},
  70#endif
  71#ifdef B57600
  72  {57600, B57600},
  73#endif
  74#ifdef B115200
  75  {115200, B115200},
  76#endif
  77#ifdef B230400
  78  {230400, B230400},
  79#endif
  80  {0, 0},
  81};
  82
  83// Find speed from mapper array 
  84static speed_t encode(char *s)
  85{
  86  struct speed_mapper *sp;
  87  long speed = atolx(s);
  88
  89  if (!speed) return 0;
  90  for (sp = speedtab; sp->speed; sp++) if (sp->speed == speed) return sp->code;
  91  return (speed_t) -1;
  92}
  93
  94static void get_speed(char *sp)
  95{
  96  char *ptr;
  97
  98  TT.sc = 0;
  99  while ((ptr = strsep(&sp, ","))) {
 100    TT.speeds[TT.sc] = encode(ptr);
 101    if (TT.speeds[TT.sc] < 0) perror_exit("bad speed");
 102    if (++TT.sc > 10) perror_exit("too many speeds, max is 10");
 103  }
 104}
 105
 106// Parse args and set TERM env. variable
 107static void parse_arguments(void)
 108{
 109  if (isdigit(**toys.optargs)) {
 110    get_speed(*toys.optargs);
 111    if (*++toys.optargs) TT.tty_name = xmprintf("%s", *toys.optargs);
 112  } else {
 113    TT.tty_name = xmprintf("%s", *toys.optargs);
 114    if (*++toys.optargs) get_speed(*toys.optargs);
 115  } 
 116  if (*++toys.optargs) setenv("TERM", *toys.optargs, 1);
 117}
 118
 119// Get controlling terminal and redirect stdio 
 120static void open_tty(void)
 121{
 122  if (strcmp(TT.tty_name, "-")) {
 123    if (*(TT.tty_name) != '/') TT.tty_name = xmprintf("/dev/%s", TT.tty_name);
 124    // Sends SIGHUP to all foreground process if Session leader don't die,Ignore
 125    sighandler_t sig = signal(SIGHUP, SIG_IGN); 
 126    ioctl(0, TIOCNOTTY, 0); // Giveup if there is any controlling terminal
 127    signal(SIGHUP, sig);
 128    if ((setsid() < 0) && (getpid() != getsid(0))) 
 129      perror_exit("setsid");
 130    xclose(0);
 131    xopen_stdio(TT.tty_name, O_RDWR|O_NDELAY|O_CLOEXEC);
 132    fcntl(0, F_SETFL, fcntl(0, F_GETFL) & ~O_NONBLOCK); // Block read
 133    dup2(0, 1);
 134    dup2(0, 2);
 135    if (ioctl(0, TIOCSCTTY, 1) < 0) perror_msg("ioctl(TIOCSCTTY)");
 136    if (!isatty(0)) perror_exit("/dev/%s: not a tty", TT.tty_name);
 137    chown(TT.tty_name, 0, 0); // change ownership, Hope login will change this
 138    chmod(TT.tty_name, 0620);
 139  } else { // We already have opened TTY
 140    if (setsid() < 0) perror_msg("setsid failed");
 141    if ((fcntl(0, F_GETFL) & (O_RDWR|O_RDONLY|O_WRONLY)) != O_RDWR)
 142      perror_exit("no read/write permission");
 143  }
 144}
 145
 146// Intialise terminal settings
 147static void termios_init(void)
 148{
 149  if (tcgetattr(STDIN_FILENO, &TT.termios) < 0) perror_exit("tcgetattr");
 150  // Flush input and output queues, important for modems!
 151  tcflush(STDIN_FILENO, TCIOFLUSH); 
 152  TT.termios.c_cflag &= (0|CSTOPB|PARENB|PARODD);
 153#ifdef CRTSCTS
 154  if (toys.optflags & FLAG_h) TT.termios.c_cflag |= CRTSCTS;
 155#endif
 156  if (toys.optflags & FLAG_L) TT.termios.c_cflag |= CLOCAL;
 157  TT.termios.c_cc[VTIME] = 0;
 158  TT.termios.c_cc[VMIN] = 1;
 159  TT.termios.c_oflag = OPOST|ONLCR;
 160  TT.termios.c_cflag |= CS8|CREAD|HUPCL|CBAUDEX;
 161  // login will disable echo for passwd.
 162  TT.termios.c_lflag |= ISIG|ICANON|ECHO|ECHOE|ECHOK|ECHOKE;
 163  TT.termios.c_cc[VINTR] = CTL('C');
 164  TT.termios.c_cc[VQUIT] = CTL('\\');
 165  TT.termios.c_cc[VEOF] = CTL('D');
 166  TT.termios.c_cc[VEOL] = '\n';
 167  TT.termios.c_cc[VKILL] = CTL('U');
 168  TT.termios.c_cc[VERASE] = 127; // CERASE
 169  TT.termios.c_iflag = ICRNL|IXON|IXOFF;
 170  // set non-zero baud rate. Zero baud rate left it unchanged.
 171  if (TT.speeds[0] != B0) cfsetspeed(&TT.termios, TT.speeds[0]); 
 172  if (tcsetattr(STDIN_FILENO, TCSANOW, &TT.termios) < 0) 
 173    perror_exit("tcsetattr");
 174}
 175
 176// Get the baud rate from modems CONNECT mesage, Its of form <junk><BAUD><Junk>
 177static void sense_baud(void)
 178{
 179  int vmin;
 180  ssize_t size;
 181  char *ptr;
 182  speed_t speed;
 183
 184  vmin = TT.termios.c_cc[VMIN]; // Store old
 185  TT.termios.c_cc[VMIN] = 0; // No block even queue is empty.
 186  if (tcsetattr(STDIN_FILENO, TCSANOW, &TT.termios) < 0) 
 187    perror_exit("tcsetattr");
 188  size = readall(STDIN_FILENO, TT.buff, sizeof(TT.buff)-1);
 189  if (size > 0) {
 190    for (ptr = TT.buff; ptr < TT.buff+size; ptr++) {
 191      if (isdigit(*ptr)) {
 192        speed = encode(ptr);
 193        if (speed > 0) cfsetspeed(&TT.termios,speed);
 194        break;
 195      }
 196    } 
 197  }
 198  TT.termios.c_cc[VMIN] = vmin; //restore old value
 199  if (tcsetattr(STDIN_FILENO, TCSANOW, &TT.termios) < 0)
 200    perror_exit("tcsetattr");
 201}
 202
 203// Just prompt for login name 
 204void print_prompt(void)
 205{
 206  char *hostname;
 207  struct utsname uts;
 208
 209  uname(&uts);
 210  hostname = xstrdup(uts.nodename);
 211  fputs(hostname, stdout);
 212  fputs(" login: ", stdout);
 213  fflush(NULL);
 214  free(hostname);
 215  hostname = NULL;
 216}
 217
 218// Print /etc/isuue with taking care of each escape sequence
 219void write_issue(char *file)
 220{
 221  char buff[20] = {0,};
 222  struct utsname u;
 223  uname(&u);
 224  int size, fd = open(TT.issue_str, O_RDONLY);
 225
 226  if (fd < 0) return;
 227  while ((size = readall(fd, buff, 1)) > 0) {
 228    char *ch = buff;
 229
 230    if (*ch == '\\' || *ch == '%') {
 231      if (readall(fd, buff, 1) <= 0) perror_exit("readall");
 232      if (*ch == 's') fputs(u.sysname, stdout);
 233      if (*ch == 'n'|| *ch == 'h') fputs(u.nodename, stdout);
 234      if (*ch == 'r') fputs(u.release, stdout);
 235      if (*ch == 'm') fputs(u.machine, stdout);
 236      if (*ch == 'l') fputs(TT.tty_name, stdout);
 237    } else xputc(*ch);
 238  }
 239}
 240
 241// Read login name and print prompt and Issue file. 
 242static int read_login_name(void)
 243{
 244  tcflush(STDIN_FILENO, TCIFLUSH); // Flush pending speed switches
 245  int i = 0;
 246
 247  while (1) { // Option -i will overide -f
 248    if (!(toys.optflags & FLAG_i)) write_issue(TT.issue_str); 
 249    print_prompt();
 250    TT.buff[0] = getchar();
 251    if (!TT.buff[0] && TT.sc > 1) return 0; // Switch speed
 252    if (TT.buff[0] == '\n') continue;
 253    if (TT.buff[0] != '\n')
 254      if (!fgets(&TT.buff[1], HOSTNAME_SIZE-1, stdin)) _exit(1);
 255    while (i < HOSTNAME_SIZE-1 && isgraph(TT.buff[i])) i++;
 256    TT.buff[i] = 0;
 257    break;
 258  }
 259  return 1;
 260}
 261
 262// Put hostname entry in utmp file
 263static void utmp_entry(void)
 264{
 265  struct utmp entry;
 266  struct utmp *utp_ptr;
 267  pid_t pid = getpid();
 268  char *utmperr = "can't make utmp entry, host length greater than UT_HOSTSIZE(256)";
 269
 270  utmpname(_PATH_UTMP);
 271  setutent(); // Starts from start
 272  while ((utp_ptr = getutent())) 
 273    if (utp_ptr->ut_pid == pid && utp_ptr->ut_type >= INIT_PROCESS) break;
 274  if (!utp_ptr) { 
 275    entry.ut_type = LOGIN_PROCESS;
 276    entry.ut_pid = getpid();
 277    xstrncpy(entry.ut_line, ttyname(STDIN_FILENO) + 
 278        strlen("/dev/"), UT_LINESIZE);
 279    time((time_t *)&entry.ut_time);
 280    xstrncpy(entry.ut_user, "LOGIN", UT_NAMESIZE);
 281    if (strlen(TT.host_str) > UT_HOSTSIZE) perror_msg_raw(utmperr);
 282    else xstrncpy(entry.ut_host, TT.host_str, UT_HOSTSIZE);
 283    setutent();
 284    pututline(&entry);
 285    return;
 286  }
 287  xstrncpy(entry.ut_line, ttyname(STDIN_FILENO) + strlen("/dev/"), UT_LINESIZE);
 288  xstrncpy(entry.ut_user, "LOGIN", UT_NAMESIZE);
 289  if (strlen(TT.host_str) > UT_HOSTSIZE) perror_msg_raw(utmperr);
 290  else xstrncpy(entry.ut_host, TT.host_str, UT_HOSTSIZE);
 291  time((time_t *)&entry.ut_time);
 292  setutent();
 293  pututline(&entry);
 294}
 295
 296void getty_main(void)
 297{
 298  pid_t pid = getpid();
 299  char *ptr[3] = {"/bin/login", NULL, NULL}; //2 NULLs so we can add username
 300
 301  if (!(toys.optflags & FLAG_f)) TT.issue_str = "/etc/issue";
 302  if (toys.optflags & FLAG_l) ptr[0] = TT.login_str;
 303  parse_arguments();
 304  open_tty();
 305  termios_init();
 306  tcsetpgrp(STDIN_FILENO, pid);
 307  if (toys.optflags & FLAG_H) utmp_entry();
 308  if (toys.optflags & FLAG_I) 
 309    writeall(STDOUT_FILENO,TT.init_str,strlen(TT.init_str));
 310  if (toys.optflags & FLAG_m) sense_baud();
 311  if (toys.optflags & FLAG_t) alarm(TT.timeout);
 312  if (toys.optflags & FLAG_w) {
 313    char ch;
 314
 315    while (readall(STDIN_FILENO, &ch, 1) != 1)  
 316      if (ch == '\n' || ch == '\r') break;
 317  }
 318  if (!(toys.optflags & FLAG_n)) {
 319    int index = 1; // 0th we already set.
 320
 321    while (1) {
 322      int l = read_login_name();
 323
 324      if (l) break;
 325      index = index % TT.sc;
 326      cfsetspeed(&TT.termios, TT.speeds[index]); // Select from multiple speeds
 327      //Necessary after cfsetspeed
 328      if (tcsetattr(STDIN_FILENO, TCSANOW, &TT.termios) < 0) 
 329        perror_exit("tcsetattr"); 
 330    }
 331    ptr[1]=TT.buff; //put the username in the login command line
 332  }
 333  xexec(ptr);
 334}
 335