336 lines
9.5 KiB
C
336 lines
9.5 KiB
C
/* 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 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 = xmprintf("%s", *toys.optargs);
|
|
} else {
|
|
TT.tty_name = xmprintf("%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 = xmprintf("/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) && (getpid() != getsid(0)))
|
|
perror_exit("setsid");
|
|
xclose(0);
|
|
xopen_stdio(TT.tty_name, O_RDWR|O_NDELAY|O_CLOEXEC);
|
|
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 tty", 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("no 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;
|
|
// 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] = 127; // 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();
|
|
char *utmperr = "can't make utmp entry, host length greater than UT_HOSTSIZE(256)";
|
|
|
|
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(utmperr);
|
|
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(utmperr);
|
|
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[3] = {"/bin/login", NULL, NULL}; //2 NULLs so we can add username
|
|
|
|
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");
|
|
}
|
|
ptr[1]=TT.buff; //put the username in the login command line
|
|
}
|
|
xexec(ptr);
|
|
}
|