aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Landley <rob@landley.net>2014-04-09 07:45:59 -0500
committerRob Landley <rob@landley.net>2014-04-09 07:45:59 -0500
commitca51eb88831521ae4df60e88d5f3cd3c4b99d46d (patch)
tree06b181bbd47257c474bf960203b64151d2633376
parent882ca8bf5b19982a641c200e473d114b78aca3c2 (diff)
downloadtoybox-ca51eb88831521ae4df60e88d5f3cd3c4b99d46d.tar.gz
A getty implementation from Sandeep Sharma and Kyungwan Han.
-rw-r--r--toys/pending/getty.c340
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