xref: /aosp_15_r20/external/toybox/toys/pending/getty.c (revision cf5a6c84e2b8763fc1a7db14496fd4742913b199)
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