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    Wait for a modem to dial into serial port, adjust baud rate, call login.
  17
  18    -h    Enable hardware RTS/CTS flow control
  19    -L    Set CLOCAL (ignore Carrier Detect state)
  20    -m    Get baud rate from modem's CONNECT status message
  21    -n    Don't prompt for login name
  22    -w    Wait for CR or LF before sending /etc/issue
  23    -i    Don't display /etc/issue
  24    -f ISSUE_FILE  Display ISSUE_FILE instead of /etc/issue
  25    -l LOGIN  Invoke LOGIN instead of /bin/login
  26    -t SEC    Terminate after SEC if no login name is read
  27    -I INITSTR  Send INITSTR before anything else
  28    -H HOST    Log HOST into the utmp file as the hostname
  29*/
  30
  31#define FOR_getty
  32#include "toys.h"
  33
  34GLOBALS(
  35  char *f, *l, *I, *H;
  36  long t;
  37
  38  char *tty_name, buff[128];
  39  int speeds[20], sc;
  40  struct termios termios;
  41)
  42
  43#define CTL(x)        ((x) ^ 0100)
  44#define HOSTNAME_SIZE 32
  45
  46static void parse_speeds(char *sp)
  47{
  48  char *ptr;
  49
  50  TT.sc = 0;
  51  while ((ptr = strsep(&sp, ","))) {
  52    TT.speeds[TT.sc] = atolx_range(ptr, 0, INT_MAX);
  53    if (TT.speeds[TT.sc] < 0) perror_exit("bad speed %s", ptr);
  54    if (++TT.sc > 10) perror_exit("too many speeds, max is 10");
  55  }
  56}
  57
  58// Get controlling terminal and redirect stdio
  59static void open_tty(void)
  60{
  61  if (strcmp(TT.tty_name, "-")) {
  62    if (*(TT.tty_name) != '/') TT.tty_name = xmprintf("/dev/%s", TT.tty_name);
  63    // Sends SIGHUP to all foreground process if Session leader don't die,Ignore
  64    void* handler = signal(SIGHUP, SIG_IGN);
  65    ioctl(0, TIOCNOTTY, 0); // Giveup if there is any controlling terminal
  66    signal(SIGHUP, handler);
  67    if ((setsid() < 0) && (getpid() != getsid(0))) perror_exit("setsid");
  68    xclose(0);
  69    xopen_stdio(TT.tty_name, O_RDWR|O_NDELAY|O_CLOEXEC);
  70    fcntl(0, F_SETFL, fcntl(0, F_GETFL) & ~O_NONBLOCK); // Block read
  71    dup2(0, 1);
  72    dup2(0, 2);
  73    if (ioctl(0, TIOCSCTTY, 1) < 0) perror_msg("ioctl(TIOCSCTTY)");
  74    if (!isatty(0)) perror_exit("/dev/%s: not a tty", TT.tty_name);
  75    chown(TT.tty_name, 0, 0); // change ownership, Hope login will change this
  76    chmod(TT.tty_name, 0620);
  77  } else { // We already have opened TTY
  78    if (setsid() < 0) perror_msg("setsid failed");
  79    if ((fcntl(0, F_GETFL) & (O_RDWR|O_RDONLY|O_WRONLY)) != O_RDWR)
  80      perror_exit("no read/write permission");
  81  }
  82}
  83
  84static void termios_init(void)
  85{
  86  if (tcgetattr(0, &TT.termios) < 0) perror_exit("tcgetattr");
  87  // Flush input and output queues, important for modems!
  88  tcflush(0, TCIOFLUSH);
  89  TT.termios.c_cflag &= (0|CSTOPB|PARENB|PARODD);
  90#ifdef CRTSCTS
  91  if (FLAG(h)) TT.termios.c_cflag |= CRTSCTS;
  92#endif
  93  if (FLAG(L)) TT.termios.c_cflag |= CLOCAL;
  94  TT.termios.c_cc[VTIME] = 0;
  95  TT.termios.c_cc[VMIN] = 1;
  96  TT.termios.c_oflag = OPOST|ONLCR;
  97  TT.termios.c_cflag |= CS8|CREAD|HUPCL|CBAUDEX;
  98  // login will disable echo for passwd.
  99  TT.termios.c_lflag |= ISIG|ICANON|ECHO|ECHOE|ECHOK|ECHOKE;
 100  TT.termios.c_cc[VINTR] = CTL('C');
 101  TT.termios.c_cc[VQUIT] = CTL('\\');
 102  TT.termios.c_cc[VEOF] = CTL('D');
 103  TT.termios.c_cc[VEOL] = '\n';
 104  TT.termios.c_cc[VKILL] = CTL('U');
 105  TT.termios.c_cc[VERASE] = 127; // CERASE
 106  TT.termios.c_iflag = ICRNL|IXON|IXOFF;
 107  // Set non-zero baud rate. Zero baud rate left it unchanged.
 108  if (TT.speeds[0] != 0) xsetspeed(&TT.termios, TT.speeds[0]);
 109  if (tcsetattr(0, TCSANOW, &TT.termios) < 0) perror_exit("tcsetattr");
 110}
 111
 112// Get the baud rate from modems CONNECT mesage, Its of form <junk><BAUD><Junk>
 113static void sense_baud(void)
 114{
 115  int vmin, speed;
 116  ssize_t size;
 117  char *ptr;
 118
 119  vmin = TT.termios.c_cc[VMIN]; // Store old
 120  TT.termios.c_cc[VMIN] = 0; // No block even queue is empty.
 121  if (tcsetattr(0, TCSANOW, &TT.termios) < 0) perror_exit("tcsetattr");
 122  size = readall(0, TT.buff, sizeof(TT.buff)-1);
 123  if (size > 0) {
 124    for (ptr = TT.buff; ptr < TT.buff+size; ptr++) {
 125      if (isdigit(*ptr)) {
 126        speed = atolx_range(ptr, 0, INT_MAX);
 127        if (speed > 0) xsetspeed(&TT.termios, speed);
 128        break;
 129      }
 130    }
 131  }
 132  TT.termios.c_cc[VMIN] = vmin; //restore old value
 133  if (tcsetattr(0, TCSANOW, &TT.termios) < 0) perror_exit("tcsetattr");
 134}
 135
 136// Print /etc/isuue with taking care of each escape sequence
 137void write_issue(char *file, struct utsname *uts)
 138{
 139  char buff[20] = {0,};
 140  int fd = open(TT.f, O_RDONLY), size;
 141
 142  if (fd < 0) return;
 143  while ((size = readall(fd, buff, 1)) > 0) {
 144    char *ch = buff;
 145
 146    if (*ch == '\\' || *ch == '%') {
 147      if (readall(fd, buff, 1) <= 0) perror_exit("readall");
 148      if (*ch == 's') fputs(uts->sysname, stdout);
 149      if (*ch == 'n'|| *ch == 'h') fputs(uts->nodename, stdout);
 150      if (*ch == 'r') fputs(uts->release, stdout);
 151      if (*ch == 'm') fputs(uts->machine, stdout);
 152      if (*ch == 'l') fputs(TT.tty_name, stdout);
 153    } else xputc(*ch);
 154  }
 155}
 156
 157// Read login name and print prompt and Issue file.
 158static int read_login_name(void)
 159{
 160  tcflush(0, TCIFLUSH); // Flush pending speed switches
 161  while (1) {
 162    struct utsname uts;
 163    int i = 0;
 164
 165    uname(&uts);
 166
 167    if (!FLAG(i)) write_issue(TT.f, &uts);
 168
 169    printf("%s login: ", uts.nodename);
 170    xflush(1);
 171
 172    TT.buff[0] = getchar();
 173    if (!TT.buff[0] && TT.sc > 1) return 0; // Switch speed
 174    if (TT.buff[0] == '\n') continue;
 175    if (TT.buff[0] != '\n')
 176      if (!fgets(&TT.buff[1], HOSTNAME_SIZE-1, stdin)) _exit(1);
 177    while (i < HOSTNAME_SIZE-1 && isgraph(TT.buff[i])) i++;
 178    TT.buff[i] = 0;
 179    break;
 180  }
 181  return 1;
 182}
 183
 184static void utmp_entry(void)
 185{
 186  struct utmpx entry = {.ut_pid = getpid()}, *ep;
 187  int fd;
 188
 189  // We're responsible for ensuring that the utmp file exists.
 190  if (access(_PATH_UTMP, F_OK) && (fd = open(_PATH_UTMP, O_CREAT, 0664)) != -1)
 191    close(fd);
 192
 193  // Find any existing entry.
 194  setutxent();
 195  while ((ep = getutxent()))
 196    if (ep->ut_pid == entry.ut_pid && ep->ut_type >= INIT_PROCESS) break;
 197  if (ep) entry = *ep;
 198  else entry.ut_type = LOGIN_PROCESS;
 199
 200  // Modify.
 201  entry.ut_tv.tv_sec = time(0);
 202  xstrncpy(entry.ut_user, "LOGIN", sizeof(entry.ut_user));
 203  xstrncpy(entry.ut_line, ttyname(0) + strlen("/dev/"), sizeof(entry.ut_line));
 204  if (FLAG(H)) xstrncpy(entry.ut_host, TT.H, sizeof(entry.ut_host));
 205
 206  // Write.
 207  pututxline(&entry);
 208  endutxent();
 209}
 210
 211void getty_main(void)
 212{
 213  char ch, *cmd[3] = {TT.l ? : "/bin/login", 0, 0}; // space to add username
 214
 215  if (!FLAG(f)) TT.f = "/etc/issue";
 216
 217  // parse arguments and set $TERM
 218  if (isdigit(**toys.optargs)) {
 219    parse_speeds(*toys.optargs);
 220    if (*++toys.optargs) TT.tty_name = xmprintf("%s", *toys.optargs);
 221  } else {
 222    TT.tty_name = xmprintf("%s", *toys.optargs);
 223    if (*++toys.optargs) parse_speeds(*toys.optargs);
 224  }
 225  if (*++toys.optargs) setenv("TERM", *toys.optargs, 1);
 226
 227  open_tty();
 228  termios_init();
 229  tcsetpgrp(0, getpid());
 230  utmp_entry();
 231  if (FLAG(I)) xputsn(TT.I);
 232  if (FLAG(m)) sense_baud();
 233  if (FLAG(t)) alarm(TT.t);
 234  if (FLAG(w)) while (readall(0, &ch, 1) != 1)  if (ch=='\n' || ch=='\r') break;
 235  if (!FLAG(n)) {
 236    int index = 1; // 0th we already set.
 237
 238    for (;;) {
 239      if (read_login_name()) break;
 240      index %= TT.sc;
 241      xsetspeed(&TT.termios, TT.speeds[index]);
 242      //Necessary after cfsetspeed
 243      if (tcsetattr(0, TCSANOW, &TT.termios) < 0) perror_exit("tcsetattr");
 244    }
 245    cmd[1] = TT.buff; //put the username in the login command line
 246  }
 247  xexec(cmd);
 248}
 249