Home | History | Annotate | Download | only in pending
      1 /* getty.c - A getty program to get controlling terminal.
      2  *
      3  * Copyright 2012 Sandeep Sharma <sandeep.jack2756 (at) gamil.com>
      4  * Copyright 2013 Kyungwan Han <asura321 (at) gmail.com>
      5  *
      6  * No Standard.
      7 
      8 USE_GETTY(NEWTOY(getty, "<2t#<0H:I:l:f:iwnmLh",TOYFLAG_SBIN))
      9 
     10 config 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 
     32 GLOBALS(
     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 
     49 typedef void (*sighandler_t)(int);
     50 struct speed_mapper {
     51   long speed;
     52   speed_t code;
     53 };
     54 
     55 struct 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
     84 static 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 
     94 static 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
    107 static 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
    120 static 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(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
    147 static 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>
    177 static 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
    204 void 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
    219 void 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.
    242 static 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
    263 static 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)
    282       perror_msg(utmperr);
    283     else xstrncpy(entry.ut_host, TT.host_str, UT_HOSTSIZE);
    284     setutent();
    285     pututline(&entry);
    286     return;
    287   }
    288   xstrncpy(entry.ut_line, ttyname(STDIN_FILENO) + strlen("/dev/"), UT_LINESIZE);
    289   xstrncpy(entry.ut_user, "LOGIN", UT_NAMESIZE);
    290   if (strlen(TT.host_str) > UT_HOSTSIZE)
    291     perror_msg(utmperr);
    292   else xstrncpy(entry.ut_host, TT.host_str, UT_HOSTSIZE);
    293   time((time_t *)&entry.ut_time);
    294   setutent();
    295   pututline(&entry);
    296 }
    297 
    298 void getty_main(void)
    299 {
    300   pid_t pid = getpid();
    301   char *ptr[3] = {"/bin/login", NULL, NULL}; //2 NULLs so we can add username
    302 
    303   if (!(toys.optflags & FLAG_f)) TT.issue_str = "/etc/issue";
    304   if (toys.optflags & FLAG_l) ptr[0] = TT.login_str;
    305   parse_arguments();
    306   open_tty();
    307   termios_init();
    308   tcsetpgrp(STDIN_FILENO, pid);
    309   if (toys.optflags & FLAG_H) utmp_entry();
    310   if (toys.optflags & FLAG_I)
    311     writeall(STDOUT_FILENO,TT.init_str,strlen(TT.init_str));
    312   if (toys.optflags & FLAG_m) sense_baud();
    313   if (toys.optflags & FLAG_t) alarm(TT.timeout);
    314   if (toys.optflags & FLAG_w) {
    315     char ch;
    316 
    317     while (readall(STDIN_FILENO, &ch, 1) != 1)
    318       if (ch == '\n' || ch == '\r') break;
    319   }
    320   if (!(toys.optflags & FLAG_n)) {
    321     int index = 1; // 0th we already set.
    322 
    323     while (1) {
    324       int l = read_login_name();
    325 
    326       if (l) break;
    327       index = index % TT.sc;
    328       cfsetspeed(&TT.termios, TT.speeds[index]); // Select from multiple speeds
    329       //Necessary after cfsetspeed
    330       if (tcsetattr(STDIN_FILENO, TCSANOW, &TT.termios) < 0)
    331         perror_exit("tcsetattr");
    332     }
    333     ptr[1]=TT.buff; //put the username in the login command line
    334   }
    335   xexec(ptr);
    336 }
    337