diff options
author | Rob Landley <rob@landley.net> | 2014-04-09 07:45:59 -0500 |
---|---|---|
committer | Rob Landley <rob@landley.net> | 2014-04-09 07:45:59 -0500 |
commit | ca51eb88831521ae4df60e88d5f3cd3c4b99d46d (patch) | |
tree | 06b181bbd47257c474bf960203b64151d2633376 | |
parent | 882ca8bf5b19982a641c200e473d114b78aca3c2 (diff) | |
download | toybox-ca51eb88831521ae4df60e88d5f3cd3c4b99d46d.tar.gz |
A getty implementation from Sandeep Sharma and Kyungwan Han.
-rw-r--r-- | toys/pending/getty.c | 340 |
1 files changed, 340 insertions, 0 deletions
diff --git a/toys/pending/getty.c b/toys/pending/getty.c new file mode 100644 index 00000000..8182923a --- /dev/null +++ b/toys/pending/getty.c @@ -0,0 +1,340 @@ +/* getty.c - A getty program to get controlling terminal. + * + * Copyright 2012 Sandeep Sharma <sandeep.jack2756@gamil.com> + * Copyright 2013 Kyungwan Han <asura321@gmail.com> + * + * No Standard. + +USE_GETTY(NEWTOY(getty, "<2t#<0H:I:l:f:iwnmLh",TOYFLAG_SBIN)) + +config GETTY + bool "getty" + default n + help + Usage: getty [OPTIONS] BAUD_RATE[,BAUD_RATE]... TTY [TERMTYPE] + + -h Enable hardware RTS/CTS flow control + -L Set CLOCAL (ignore Carrier Detect state) + -m Get baud rate from modem's CONNECT status message + -n Don't prompt for login name + -w Wait for CR or LF before sending /etc/issue + -i Don't display /etc/issue + -f ISSUE_FILE Display ISSUE_FILE instead of /etc/issue + -l LOGIN Invoke LOGIN instead of /bin/login + -t SEC Terminate after SEC if no login name is read + -I INITSTR Send INITSTR before anything else + -H HOST Log HOST into the utmp file as the hostname +*/ +#define FOR_getty +#include "toys.h" +#include <utmp.h> + +GLOBALS( + char *issue_str; + char *login_str; + char *init_str; + char *host_str; + long timeout; + + char *tty_name; + int speeds[20]; + int sc; + struct termios termios; + char buff[128]; +) + +#define CTL(x) ((x) ^ 0100) +#define HOSTNAME_SIZE 32 + +typedef void (*sighandler_t)(int); +struct speed_mapper { + long speed; + speed_t code; +}; + +struct speed_mapper speedtab[] = { + {50, B50}, {75, B75}, {110, B110}, {134, B134}, {150, B150}, {200, B200}, + {300, B300}, {600, B600}, {1200, B1200}, {1800, B1800}, {2400, B2400}, + {4800, B4800}, {9600, B9600}, +#ifdef B19200 + {19200, B19200}, +#endif +#ifdef B38400 + {38400, B38400}, +#endif +#ifdef EXTA + {19200, EXTA}, +#endif +#ifdef EXTB + {38400, B38400}, +#endif +#ifdef B57600 + {57600, B57600}, +#endif +#ifdef B115200 + {115200, B115200}, +#endif +#ifdef B230400 + {230400, B230400}, +#endif + {0, 0}, +}; + +// Find speed from mapper array +static speed_t encode(char *s) +{ + struct speed_mapper *sp; + long speed = atolx(s); + + if (!speed) return 0; + for (sp = speedtab; sp->speed; sp++) if (sp->speed == speed) return sp->code; + return (speed_t) -1; +} + +static void get_speed(char *sp) +{ + char *ptr; + + TT.sc = 0; + while ((ptr = strsep(&sp, ","))) { + TT.speeds[TT.sc] = encode(ptr); + if (TT.speeds[TT.sc] < 0) perror_exit("Bad Speed"); + if (++TT.sc > 10) perror_exit("Too many alternate speeds, Max is 10"); + } +} + +// Parse args and set TERM env. variable +static void parse_arguments(void) +{ + if (isdigit(**toys.optargs)) { + get_speed(*toys.optargs); + if (*++toys.optargs) TT.tty_name = xmsprintf("%s", *toys.optargs); + } else { + TT.tty_name = xmsprintf("%s", *toys.optargs); + if (*++toys.optargs) get_speed(*toys.optargs); + } + if (*++toys.optargs) setenv("TERM", *toys.optargs, 1); +} + +// Get controlling terminal and redirect stdio +static void open_tty(void) +{ + if (strcmp(TT.tty_name, "-")) { + if (*(TT.tty_name) != '/') TT.tty_name = xmsprintf("/dev/%s", TT.tty_name); + // Sends SIGHUP to all foreground process if Session leader don't die,Ignore + sighandler_t sig = signal(SIGHUP, SIG_IGN); + ioctl(0, TIOCNOTTY, 0); // Giveup if there is any controlling terminal + signal(SIGHUP, sig); + if (setsid() < 0) { // Seems we are session leader + pid_t sid = getpid(); + + if(sid != getsid(0)) perror_exit("setsid"); + } + xclose(0); + xopen(TT.tty_name, O_RDWR|O_NDELAY); + fcntl(0, F_SETFL, fcntl(0, F_GETFL) & ~O_NONBLOCK); // Block read + dup2(0, 1); + dup2(0, 2); + if (ioctl(0, TIOCSCTTY, 1) < 0) perror_msg("ioctl(TIOCSCTTY)"); + if (!isatty(0)) perror_exit("/dev/%s:Not a character device", TT.tty_name); + chown(TT.tty_name, 0, 0); // change ownership, Hope login will change this + chmod(TT.tty_name, 0620); + } else { // We already have opened TTY + if (setsid() < 0) perror_msg("setsid:failed"); + if ((fcntl(0, F_GETFL) & (O_RDWR|O_RDONLY|O_WRONLY)) != O_RDWR) + perror_exit("opened tty don't have read/write permission"); + } +} + +// Intialise terminal settings +static void termios_init(void) +{ + if (tcgetattr(STDIN_FILENO, &TT.termios) < 0) perror_exit("tcgetattr"); + // Flush input and output queues, important for modems! + tcflush(STDIN_FILENO, TCIOFLUSH); + TT.termios.c_cflag &= (0|CSTOPB|PARENB|PARODD); +#ifdef CRTSCTS + if (toys.optflags & FLAG_h) TT.termios.c_cflag |= CRTSCTS; +#endif + if (toys.optflags & FLAG_L) TT.termios.c_cflag |= CLOCAL; + TT.termios.c_cc[VTIME] = 0; + TT.termios.c_cc[VMIN] = 1; + TT.termios.c_oflag = OPOST|ONLCR; + TT.termios.c_cflag |= CS8|CREAD|HUPCL|CBAUDEX; + TT.termios.c_iflag = 0; + // login will disable echo for passwd. + TT.termios.c_lflag |= ISIG|ICANON|ECHO|ECHOE|ECHOK|ECHOKE; + TT.termios.c_cc[VINTR] = CTL('C'); + TT.termios.c_cc[VQUIT] = CTL('\\'); + TT.termios.c_cc[VEOF] = CTL('D'); + TT.termios.c_cc[VEOL] = '\n'; + TT.termios.c_cc[VKILL] = CTL('U'); + TT.termios.c_cc[VERASE] = CERASE; + TT.termios.c_iflag |= ICRNL|IXON|IXOFF; + // set non-zero baud rate. Zero baud rate left it unchanged. + if (TT.speeds[0] != B0) cfsetspeed(&TT.termios, TT.speeds[0]); + if (tcsetattr(STDIN_FILENO, TCSANOW, &TT.termios) < 0) + perror_exit("tcsetattr"); +} + +// Get the baud rate from modems CONNECT mesage, Its of form <junk><BAUD><Junk> +static void sense_baud(void) +{ + int vmin; + ssize_t size; + char *ptr; + speed_t speed; + + vmin = TT.termios.c_cc[VMIN]; // Store old + TT.termios.c_cc[VMIN] = 0; // No block even queue is empty. + if (tcsetattr(STDIN_FILENO, TCSANOW, &TT.termios) < 0) + perror_exit("tcsetattr"); + size = readall(STDIN_FILENO, TT.buff, sizeof(TT.buff)-1); + if (size > 0) { + for (ptr = TT.buff; ptr < TT.buff+size; ptr++) { + if (isdigit(*ptr)) { + speed = encode(ptr); + if (speed > 0) cfsetspeed(&TT.termios,speed); + break; + } + } + } + TT.termios.c_cc[VMIN] = vmin; //restore old value + if (tcsetattr(STDIN_FILENO, TCSANOW, &TT.termios) < 0) + perror_exit("tcsetattr"); +} + +// Just prompt for login name +void print_prompt(void) +{ + char *hostname; + struct utsname uts; + + uname(&uts); + hostname = xstrdup(uts.nodename); + fputs(hostname, stdout); + fputs(" Login: ", stdout); + fflush(NULL); + free(hostname); + hostname = NULL; +} + +// Print /etc/isuue with taking care of each escape sequence +void write_issue(char *file) +{ + char buff[20] = {0,}; + struct utsname u; + uname(&u); + int size, fd = open(TT.issue_str, O_RDONLY); + + if (fd < 0) return; + while ((size = readall(fd, buff, 1)) > 0) { + char *ch = buff; + + if (*ch == '\\' || *ch == '%') { + if (readall(fd, buff, 1) <= 0) perror_exit("readall"); + if (*ch == 's') fputs(u.sysname, stdout); + if (*ch == 'n'|| *ch == 'h') fputs(u.nodename, stdout); + if (*ch == 'r') fputs(u.release, stdout); + if (*ch == 'm') fputs(u.machine, stdout); + if (*ch == 'l') fputs(TT.tty_name, stdout); + } else xputc(*ch); + } +} + +// Read login name and print prompt and Issue file. +static int read_login_name(void) +{ + tcflush(STDIN_FILENO, TCIFLUSH); // Flush pending speed switches + int i = 0; + + while (1) { // Option -i will overide -f + if (!(toys.optflags & FLAG_i)) write_issue(TT.issue_str); + print_prompt(); + TT.buff[0] = getchar(); + if (!TT.buff[0] && TT.sc > 1) return 0; // Switch speed + if (TT.buff[0] == '\n') continue; + if (TT.buff[0] != '\n') + if (!fgets(&TT.buff[1], HOSTNAME_SIZE-1, stdin)) _exit(1); + while (i < HOSTNAME_SIZE-1 && isgraph(TT.buff[i])) i++; + TT.buff[i] = 0; + break; + } + return 1; +} + +// Put hostname entry in utmp file +static void utmp_entry(void) +{ + struct utmp entry; + struct utmp *utp_ptr; + pid_t pid = getpid(); + + utmpname(_PATH_UTMP); + setutent(); // Starts from start + while ((utp_ptr = getutent())) + if (utp_ptr->ut_pid == pid && utp_ptr->ut_type >= INIT_PROCESS) break; + if (!utp_ptr) { + entry.ut_type = LOGIN_PROCESS; + entry.ut_pid = getpid(); + xstrncpy(entry.ut_line, ttyname(STDIN_FILENO) + + strlen("/dev/"), UT_LINESIZE); + time((time_t *)&entry.ut_time); + xstrncpy(entry.ut_user, "LOGIN", UT_NAMESIZE); + if (strlen(TT.host_str) > UT_HOSTSIZE) + perror_msg("Can't make utmp entry, Host length is greater than UT_HOSTSIZE(256)"); + else xstrncpy(entry.ut_host, TT.host_str, UT_HOSTSIZE); + setutent(); + pututline(&entry); + return; + } + xstrncpy(entry.ut_line, ttyname(STDIN_FILENO) + strlen("/dev/"), UT_LINESIZE); + xstrncpy(entry.ut_user, "LOGIN", UT_NAMESIZE); + if (strlen(TT.host_str) > UT_HOSTSIZE) + perror_msg("Can't make utmp entry,Host length is greater than UT_HOSTSIZE(256)"); + else xstrncpy(entry.ut_host, TT.host_str, UT_HOSTSIZE); + time((time_t *)&entry.ut_time); + setutent(); + pututline(&entry); +} + +void getty_main(void) +{ + pid_t pid = getpid(); + char *ptr[2] = {"/bin/login", NULL}; + + if (!(toys.optflags & FLAG_f)) TT.issue_str = "/etc/issue"; + if (toys.optflags & FLAG_l) ptr[0] = TT.login_str; + parse_arguments(); + open_tty(); + termios_init(); + tcsetpgrp(STDIN_FILENO, pid); + if (toys.optflags & FLAG_H) utmp_entry(); + if (toys.optflags & FLAG_I) + writeall(STDOUT_FILENO,TT.init_str,strlen(TT.init_str)); + if (toys.optflags & FLAG_m) sense_baud(); + if (toys.optflags & FLAG_t) alarm(TT.timeout); + if (toys.optflags & FLAG_w) { + char ch; + + while (readall(STDIN_FILENO, &ch, 1) != 1) + if (ch == '\n' || ch == '\r') break; + } + if (!(toys.optflags & FLAG_n)) { + int index = 1; // 0th we already set. + + while (1) { + int l = read_login_name(); + + if (l) break; + index = index % TT.sc; + cfsetspeed(&TT.termios, TT.speeds[index]); // Select from multiple speeds + //Necessary after cfsetspeed + if (tcsetattr(STDIN_FILENO, TCSANOW, &TT.termios) < 0) + perror_exit("tcsetattr"); + } + } + if (toys.optflags & FLAG_n) execlp(*ptr, *ptr ,NULL); + else execlp(*ptr, *ptr, TT.buff, NULL); + perror_exit("error:%d",errno); // exec will return only if error +}
\ No newline at end of file |