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