1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28#define FOR_getty
29#include "toys.h"
30#include <utmp.h>
31
32GLOBALS(
33 char *issue_str;
34 char *login_str;
35 char *init_str;
36 char *host_str;
37 long timeout;
38
39 char *tty_name;
40 int speeds[20];
41 int sc;
42 struct termios termios;
43 char buff[128];
44)
45
46#define CTL(x) ((x) ^ 0100)
47#define HOSTNAME_SIZE 32
48
49typedef void (*sighandler_t)(int);
50struct speed_mapper {
51 long speed;
52 speed_t code;
53};
54
55struct speed_mapper speedtab[] = {
56 {50, B50}, {75, B75}, {110, B110}, {134, B134}, {150, B150}, {200, B200},
57 {300, B300}, {600, B600}, {1200, B1200}, {1800, B1800}, {2400, B2400},
58 {4800, B4800}, {9600, B9600},
59#ifdef B19200
60 {19200, B19200},
61#endif
62#ifdef B38400
63 {38400, B38400},
64#endif
65#ifdef EXTA
66 {19200, EXTA},
67#endif
68#ifdef EXTB
69 {38400, B38400},
70#endif
71#ifdef B57600
72 {57600, B57600},
73#endif
74#ifdef B115200
75 {115200, B115200},
76#endif
77#ifdef B230400
78 {230400, B230400},
79#endif
80 {0, 0},
81};
82
83
84static speed_t encode(char *s)
85{
86 struct speed_mapper *sp;
87 long speed = atolx(s);
88
89 if (!speed) return 0;
90 for (sp = speedtab; sp->speed; sp++) if (sp->speed == speed) return sp->code;
91 return (speed_t) -1;
92}
93
94static void get_speed(char *sp)
95{
96 char *ptr;
97
98 TT.sc = 0;
99 while ((ptr = strsep(&sp, ","))) {
100 TT.speeds[TT.sc] = encode(ptr);
101 if (TT.speeds[TT.sc] < 0) perror_exit("bad speed");
102 if (++TT.sc > 10) perror_exit("too many speeds, max is 10");
103 }
104}
105
106
107static void parse_arguments(void)
108{
109 if (isdigit(**toys.optargs)) {
110 get_speed(*toys.optargs);
111 if (*++toys.optargs) TT.tty_name = xmprintf("%s", *toys.optargs);
112 } else {
113 TT.tty_name = xmprintf("%s", *toys.optargs);
114 if (*++toys.optargs) get_speed(*toys.optargs);
115 }
116 if (*++toys.optargs) setenv("TERM", *toys.optargs, 1);
117}
118
119
120static void open_tty(void)
121{
122 if (strcmp(TT.tty_name, "-")) {
123 if (*(TT.tty_name) != '/') TT.tty_name = xmprintf("/dev/%s", TT.tty_name);
124
125 sighandler_t sig = signal(SIGHUP, SIG_IGN);
126 ioctl(0, TIOCNOTTY, 0);
127 signal(SIGHUP, sig);
128 if ((setsid() < 0) && (getpid() != getsid(0)))
129 perror_exit("setsid");
130 xclose(0);
131 xopen_stdio(TT.tty_name, O_RDWR|O_NDELAY|O_CLOEXEC);
132 fcntl(0, F_SETFL, fcntl(0, F_GETFL) & ~O_NONBLOCK);
133 dup2(0, 1);
134 dup2(0, 2);
135 if (ioctl(0, TIOCSCTTY, 1) < 0) perror_msg("ioctl(TIOCSCTTY)");
136 if (!isatty(0)) perror_exit("/dev/%s: not a tty", TT.tty_name);
137 chown(TT.tty_name, 0, 0);
138 chmod(TT.tty_name, 0620);
139 } else {
140 if (setsid() < 0) perror_msg("setsid failed");
141 if ((fcntl(0, F_GETFL) & (O_RDWR|O_RDONLY|O_WRONLY)) != O_RDWR)
142 perror_exit("no read/write permission");
143 }
144}
145
146
147static void termios_init(void)
148{
149 if (tcgetattr(STDIN_FILENO, &TT.termios) < 0) perror_exit("tcgetattr");
150
151 tcflush(STDIN_FILENO, TCIOFLUSH);
152 TT.termios.c_cflag &= (0|CSTOPB|PARENB|PARODD);
153#ifdef CRTSCTS
154 if (toys.optflags & FLAG_h) TT.termios.c_cflag |= CRTSCTS;
155#endif
156 if (toys.optflags & FLAG_L) TT.termios.c_cflag |= CLOCAL;
157 TT.termios.c_cc[VTIME] = 0;
158 TT.termios.c_cc[VMIN] = 1;
159 TT.termios.c_oflag = OPOST|ONLCR;
160 TT.termios.c_cflag |= CS8|CREAD|HUPCL|CBAUDEX;
161
162 TT.termios.c_lflag |= ISIG|ICANON|ECHO|ECHOE|ECHOK|ECHOKE;
163 TT.termios.c_cc[VINTR] = CTL('C');
164 TT.termios.c_cc[VQUIT] = CTL('\\');
165 TT.termios.c_cc[VEOF] = CTL('D');
166 TT.termios.c_cc[VEOL] = '\n';
167 TT.termios.c_cc[VKILL] = CTL('U');
168 TT.termios.c_cc[VERASE] = 127;
169 TT.termios.c_iflag = ICRNL|IXON|IXOFF;
170
171 if (TT.speeds[0] != B0) cfsetspeed(&TT.termios, TT.speeds[0]);
172 if (tcsetattr(STDIN_FILENO, TCSANOW, &TT.termios) < 0)
173 perror_exit("tcsetattr");
174}
175
176
177static void sense_baud(void)
178{
179 int vmin;
180 ssize_t size;
181 char *ptr;
182 speed_t speed;
183
184 vmin = TT.termios.c_cc[VMIN];
185 TT.termios.c_cc[VMIN] = 0;
186 if (tcsetattr(STDIN_FILENO, TCSANOW, &TT.termios) < 0)
187 perror_exit("tcsetattr");
188 size = readall(STDIN_FILENO, TT.buff, sizeof(TT.buff)-1);
189 if (size > 0) {
190 for (ptr = TT.buff; ptr < TT.buff+size; ptr++) {
191 if (isdigit(*ptr)) {
192 speed = encode(ptr);
193 if (speed > 0) cfsetspeed(&TT.termios,speed);
194 break;
195 }
196 }
197 }
198 TT.termios.c_cc[VMIN] = vmin;
199 if (tcsetattr(STDIN_FILENO, TCSANOW, &TT.termios) < 0)
200 perror_exit("tcsetattr");
201}
202
203
204void print_prompt(void)
205{
206 char *hostname;
207 struct utsname uts;
208
209 uname(&uts);
210 hostname = xstrdup(uts.nodename);
211 fputs(hostname, stdout);
212 fputs(" login: ", stdout);
213 fflush(NULL);
214 free(hostname);
215 hostname = NULL;
216}
217
218
219void write_issue(char *file)
220{
221 char buff[20] = {0,};
222 struct utsname u;
223 uname(&u);
224 int size, fd = open(TT.issue_str, O_RDONLY);
225
226 if (fd < 0) return;
227 while ((size = readall(fd, buff, 1)) > 0) {
228 char *ch = buff;
229
230 if (*ch == '\\' || *ch == '%') {
231 if (readall(fd, buff, 1) <= 0) perror_exit("readall");
232 if (*ch == 's') fputs(u.sysname, stdout);
233 if (*ch == 'n'|| *ch == 'h') fputs(u.nodename, stdout);
234 if (*ch == 'r') fputs(u.release, stdout);
235 if (*ch == 'm') fputs(u.machine, stdout);
236 if (*ch == 'l') fputs(TT.tty_name, stdout);
237 } else xputc(*ch);
238 }
239}
240
241
242static int read_login_name(void)
243{
244 tcflush(STDIN_FILENO, TCIFLUSH);
245 int i = 0;
246
247 while (1) {
248 if (!(toys.optflags & FLAG_i)) write_issue(TT.issue_str);
249 print_prompt();
250 TT.buff[0] = getchar();
251 if (!TT.buff[0] && TT.sc > 1) return 0;
252 if (TT.buff[0] == '\n') continue;
253 if (TT.buff[0] != '\n')
254 if (!fgets(&TT.buff[1], HOSTNAME_SIZE-1, stdin)) _exit(1);
255 while (i < HOSTNAME_SIZE-1 && isgraph(TT.buff[i])) i++;
256 TT.buff[i] = 0;
257 break;
258 }
259 return 1;
260}
261
262
263static void utmp_entry(void)
264{
265 struct utmp entry;
266 struct utmp *utp_ptr;
267 pid_t pid = getpid();
268 char *utmperr = "can't make utmp entry, host length greater than UT_HOSTSIZE(256)";
269
270 utmpname(_PATH_UTMP);
271 setutent();
272 while ((utp_ptr = getutent()))
273 if (utp_ptr->ut_pid == pid && utp_ptr->ut_type >= INIT_PROCESS) break;
274 if (!utp_ptr) {
275 entry.ut_type = LOGIN_PROCESS;
276 entry.ut_pid = getpid();
277 xstrncpy(entry.ut_line, ttyname(STDIN_FILENO) +
278 strlen("/dev/"), UT_LINESIZE);
279 time((time_t *)&entry.ut_time);
280 xstrncpy(entry.ut_user, "LOGIN", UT_NAMESIZE);
281 if (strlen(TT.host_str) > UT_HOSTSIZE) perror_msg_raw(utmperr);
282 else xstrncpy(entry.ut_host, TT.host_str, UT_HOSTSIZE);
283 setutent();
284 pututline(&entry);
285 return;
286 }
287 xstrncpy(entry.ut_line, ttyname(STDIN_FILENO) + strlen("/dev/"), UT_LINESIZE);
288 xstrncpy(entry.ut_user, "LOGIN", UT_NAMESIZE);
289 if (strlen(TT.host_str) > UT_HOSTSIZE) perror_msg_raw(utmperr);
290 else xstrncpy(entry.ut_host, TT.host_str, UT_HOSTSIZE);
291 time((time_t *)&entry.ut_time);
292 setutent();
293 pututline(&entry);
294}
295
296void getty_main(void)
297{
298 pid_t pid = getpid();
299 char *ptr[3] = {"/bin/login", NULL, NULL};
300
301 if (!(toys.optflags & FLAG_f)) TT.issue_str = "/etc/issue";
302 if (toys.optflags & FLAG_l) ptr[0] = TT.login_str;
303 parse_arguments();
304 open_tty();
305 termios_init();
306 tcsetpgrp(STDIN_FILENO, pid);
307 if (toys.optflags & FLAG_H) utmp_entry();
308 if (toys.optflags & FLAG_I)
309 writeall(STDOUT_FILENO,TT.init_str,strlen(TT.init_str));
310 if (toys.optflags & FLAG_m) sense_baud();
311 if (toys.optflags & FLAG_t) alarm(TT.timeout);
312 if (toys.optflags & FLAG_w) {
313 char ch;
314
315 while (readall(STDIN_FILENO, &ch, 1) != 1)
316 if (ch == '\n' || ch == '\r') break;
317 }
318 if (!(toys.optflags & FLAG_n)) {
319 int index = 1;
320
321 while (1) {
322 int l = read_login_name();
323
324 if (l) break;
325 index = index % TT.sc;
326 cfsetspeed(&TT.termios, TT.speeds[index]);
327
328 if (tcsetattr(STDIN_FILENO, TCSANOW, &TT.termios) < 0)
329 perror_exit("tcsetattr");
330 }
331 ptr[1]=TT.buff;
332 }
333 xexec(ptr);
334}
335