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
29
30
31#define FOR_getty
32#include "toys.h"
33
34GLOBALS(
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)
42
43#define CTL(x) ((x) ^ 0100)
44#define HOSTNAME_SIZE 32
45
46static void parse_speeds(char *sp)
47{
48 char *ptr;
49
50 TT.sc = 0;
51 while ((ptr = strsep(&sp, ","))) {
52 TT.speeds[TT.sc] = atolx_range(ptr, 0, INT_MAX);
53 if (TT.speeds[TT.sc] < 0) perror_exit("bad speed %s", ptr);
54 if (++TT.sc > 10) perror_exit("too many speeds, max is 10");
55 }
56}
57
58
59static void open_tty(void)
60{
61 if (strcmp(TT.tty_name, "-")) {
62 if (*(TT.tty_name) != '/') TT.tty_name = xmprintf("/dev/%s", TT.tty_name);
63
64 void* handler = signal(SIGHUP, SIG_IGN);
65 ioctl(0, TIOCNOTTY, 0);
66 signal(SIGHUP, handler);
67 if ((setsid() < 0) && (getpid() != getsid(0))) perror_exit("setsid");
68 xclose(0);
69 xopen_stdio(TT.tty_name, O_RDWR|O_NDELAY|O_CLOEXEC);
70 fcntl(0, F_SETFL, fcntl(0, F_GETFL) & ~O_NONBLOCK);
71 dup2(0, 1);
72 dup2(0, 2);
73 if (ioctl(0, TIOCSCTTY, 1) < 0) perror_msg("ioctl(TIOCSCTTY)");
74 if (!isatty(0)) perror_exit("/dev/%s: not a tty", TT.tty_name);
75 chown(TT.tty_name, 0, 0);
76 chmod(TT.tty_name, 0620);
77 } else {
78 if (setsid() < 0) perror_msg("setsid failed");
79 if ((fcntl(0, F_GETFL) & (O_RDWR|O_RDONLY|O_WRONLY)) != O_RDWR)
80 perror_exit("no read/write permission");
81 }
82}
83
84static void termios_init(void)
85{
86 if (tcgetattr(0, &TT.termios) < 0) perror_exit("tcgetattr");
87
88 tcflush(0, TCIOFLUSH);
89 TT.termios.c_cflag &= (0|CSTOPB|PARENB|PARODD);
90#ifdef CRTSCTS
91 if (FLAG(h)) TT.termios.c_cflag |= CRTSCTS;
92#endif
93 if (FLAG(L)) TT.termios.c_cflag |= CLOCAL;
94 TT.termios.c_cc[VTIME] = 0;
95 TT.termios.c_cc[VMIN] = 1;
96 TT.termios.c_oflag = OPOST|ONLCR;
97 TT.termios.c_cflag |= CS8|CREAD|HUPCL|CBAUDEX;
98
99 TT.termios.c_lflag |= ISIG|ICANON|ECHO|ECHOE|ECHOK|ECHOKE;
100 TT.termios.c_cc[VINTR] = CTL('C');
101 TT.termios.c_cc[VQUIT] = CTL('\\');
102 TT.termios.c_cc[VEOF] = CTL('D');
103 TT.termios.c_cc[VEOL] = '\n';
104 TT.termios.c_cc[VKILL] = CTL('U');
105 TT.termios.c_cc[VERASE] = 127;
106 TT.termios.c_iflag = ICRNL|IXON|IXOFF;
107
108 if (TT.speeds[0] != 0) xsetspeed(&TT.termios, TT.speeds[0]);
109 if (tcsetattr(0, TCSANOW, &TT.termios) < 0) perror_exit("tcsetattr");
110}
111
112
113static void sense_baud(void)
114{
115 int vmin, speed;
116 ssize_t size;
117 char *ptr;
118
119 vmin = TT.termios.c_cc[VMIN];
120 TT.termios.c_cc[VMIN] = 0;
121 if (tcsetattr(0, TCSANOW, &TT.termios) < 0) perror_exit("tcsetattr");
122 size = readall(0, TT.buff, sizeof(TT.buff)-1);
123 if (size > 0) {
124 for (ptr = TT.buff; ptr < TT.buff+size; ptr++) {
125 if (isdigit(*ptr)) {
126 speed = atolx_range(ptr, 0, INT_MAX);
127 if (speed > 0) xsetspeed(&TT.termios, speed);
128 break;
129 }
130 }
131 }
132 TT.termios.c_cc[VMIN] = vmin;
133 if (tcsetattr(0, TCSANOW, &TT.termios) < 0) perror_exit("tcsetattr");
134}
135
136
137void write_issue(char *file, struct utsname *uts)
138{
139 char buff[20] = {0,};
140 int fd = open(TT.f, O_RDONLY), size;
141
142 if (fd < 0) return;
143 while ((size = readall(fd, buff, 1)) > 0) {
144 char *ch = buff;
145
146 if (*ch == '\\' || *ch == '%') {
147 if (readall(fd, buff, 1) <= 0) perror_exit("readall");
148 if (*ch == 's') fputs(uts->sysname, stdout);
149 if (*ch == 'n'|| *ch == 'h') fputs(uts->nodename, stdout);
150 if (*ch == 'r') fputs(uts->release, stdout);
151 if (*ch == 'm') fputs(uts->machine, stdout);
152 if (*ch == 'l') fputs(TT.tty_name, stdout);
153 } else xputc(*ch);
154 }
155}
156
157
158static int read_login_name(void)
159{
160 tcflush(0, TCIFLUSH);
161 while (1) {
162 struct utsname uts;
163 int i = 0;
164
165 uname(&uts);
166
167 if (!FLAG(i)) write_issue(TT.f, &uts);
168
169 printf("%s login: ", uts.nodename);
170 xflush(1);
171
172 TT.buff[0] = getchar();
173 if (!TT.buff[0] && TT.sc > 1) return 0;
174 if (TT.buff[0] == '\n') continue;
175 if (TT.buff[0] != '\n')
176 if (!fgets(&TT.buff[1], HOSTNAME_SIZE-1, stdin)) _exit(1);
177 while (i < HOSTNAME_SIZE-1 && isgraph(TT.buff[i])) i++;
178 TT.buff[i] = 0;
179 break;
180 }
181 return 1;
182}
183
184static void utmp_entry(void)
185{
186 struct utmpx entry = {.ut_pid = getpid()}, *ep;
187 int fd;
188
189
190 if (access(_PATH_UTMP, F_OK) && (fd = open(_PATH_UTMP, O_CREAT, 0664)) != -1)
191 close(fd);
192
193
194 setutxent();
195 while ((ep = getutxent()))
196 if (ep->ut_pid == entry.ut_pid && ep->ut_type >= INIT_PROCESS) break;
197 if (ep) entry = *ep;
198 else entry.ut_type = LOGIN_PROCESS;
199
200
201 entry.ut_tv.tv_sec = time(0);
202 xstrncpy(entry.ut_user, "LOGIN", sizeof(entry.ut_user));
203 xstrncpy(entry.ut_line, ttyname(0) + strlen("/dev/"), sizeof(entry.ut_line));
204 if (FLAG(H)) xstrncpy(entry.ut_host, TT.H, sizeof(entry.ut_host));
205
206
207 pututxline(&entry);
208 endutxent();
209}
210
211void getty_main(void)
212{
213 char ch, *cmd[3] = {TT.l ? : "/bin/login", 0, 0};
214
215 if (!FLAG(f)) TT.f = "/etc/issue";
216
217
218 if (isdigit(**toys.optargs)) {
219 parse_speeds(*toys.optargs);
220 if (*++toys.optargs) TT.tty_name = xmprintf("%s", *toys.optargs);
221 } else {
222 TT.tty_name = xmprintf("%s", *toys.optargs);
223 if (*++toys.optargs) parse_speeds(*toys.optargs);
224 }
225 if (*++toys.optargs) setenv("TERM", *toys.optargs, 1);
226
227 open_tty();
228 termios_init();
229 tcsetpgrp(0, getpid());
230 utmp_entry();
231 if (FLAG(I)) xputsn(TT.I);
232 if (FLAG(m)) sense_baud();
233 if (FLAG(t)) alarm(TT.t);
234 if (FLAG(w)) while (readall(0, &ch, 1) != 1) if (ch=='\n' || ch=='\r') break;
235 if (!FLAG(n)) {
236 int index = 1;
237
238 for (;;) {
239 if (read_login_name()) break;
240 index %= TT.sc;
241 xsetspeed(&TT.termios, TT.speeds[index]);
242
243 if (tcsetattr(0, TCSANOW, &TT.termios) < 0) perror_exit("tcsetattr");
244 }
245 cmd[1] = TT.buff;
246 }
247 xexec(cmd);
248}
249