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