]>
Commit | Line | Data |
---|---|---|
b75c8134 | 1 | /* |
53d55042 SK |
2 | * Alternate Getty (agetty) 'agetty' is a versatile, portable, easy to use |
3 | * replacement for getty on SunOS 4.1.x or the SAC ttymon/ttyadm/sacadm/pmadm | |
4 | * suite on Solaris and other SVR4 systems. 'agetty' was written by Wietse | |
5 | * Venema, enhanced by John DiMarco, and further enhanced by Dennis Cronin. | |
b75c8134 KZ |
6 | * |
7 | * Ported to Linux by Peter Orbaek <poe@daimi.aau.dk> | |
b75c8134 | 8 | * |
53d55042 | 9 | * This program is freely distributable. |
b75c8134 | 10 | */ |
53d55042 | 11 | |
6dbe3af9 KZ |
12 | #include <stdio.h> |
13 | #include <unistd.h> | |
14 | #include <stdlib.h> | |
2b6fc908 | 15 | #include <string.h> |
1961482a | 16 | #include <termios.h> |
6dbe3af9 KZ |
17 | #include <signal.h> |
18 | #include <errno.h> | |
19 | #include <sys/types.h> | |
20 | #include <sys/stat.h> | |
21 | #include <fcntl.h> | |
22853e4a | 22 | #include <stdarg.h> |
6dbe3af9 KZ |
23 | #include <ctype.h> |
24 | #include <utmp.h> | |
25 | #include <getopt.h> | |
22853e4a | 26 | #include <time.h> |
6dbe3af9 | 27 | #include <sys/file.h> |
e61e66bd KZ |
28 | #include <sys/socket.h> |
29 | #include <netdb.h> | |
df73ad46 | 30 | |
8abcf290 | 31 | #include "strutils.h" |
7eda085c | 32 | #include "nls.h" |
e6590f06 | 33 | #include "pathnames.h" |
e5b17b31 | 34 | #include "c.h" |
be66a93a | 35 | #include "xalloc.h" |
6dbe3af9 | 36 | |
fd6b7a7f | 37 | #ifdef __linux__ |
53d55042 SK |
38 | # include <sys/param.h> |
39 | # define USE_SYSLOG | |
6dbe3af9 KZ |
40 | #endif |
41 | ||
53d55042 | 42 | /* If USE_SYSLOG is undefined all diagnostics go to /dev/console. */ |
6dbe3af9 | 43 | #ifdef USE_SYSLOG |
53d55042 | 44 | # include <syslog.h> |
6dbe3af9 KZ |
45 | #endif |
46 | ||
b75c8134 KZ |
47 | /* |
48 | * Some heuristics to find out what environment we are in: if it is not | |
53d55042 SK |
49 | * System V, assume it is SunOS 4. The LOGIN_PROCESS is defined in System V |
50 | * utmp.h, which will select System V style getty. | |
b75c8134 | 51 | */ |
53d55042 SK |
52 | #ifdef LOGIN_PROCESS |
53 | # define SYSV_STYLE | |
6dbe3af9 KZ |
54 | #endif |
55 | ||
b75c8134 KZ |
56 | /* |
57 | * Things you may want to modify. | |
58 | * | |
59 | * If ISSUE is not defined, agetty will never display the contents of the | |
60 | * /etc/issue file. You will not want to spit out large "issue" files at the | |
61 | * wrong baud rate. Relevant for System V only. | |
62 | * | |
63 | * You may disagree with the default line-editing etc. characters defined | |
64 | * below. Note, however, that DEL cannot be used for interrupt generation | |
65 | * and for line editing at the same time. | |
66 | */ | |
6dbe3af9 | 67 | |
53d55042 | 68 | /* Displayed before the login prompt. */ |
6dbe3af9 | 69 | #ifdef SYSV_STYLE |
53d55042 SK |
70 | # define ISSUE "/etc/issue" |
71 | # include <sys/utsname.h> | |
6dbe3af9 KZ |
72 | #endif |
73 | ||
53d55042 SK |
74 | /* Login prompt. */ |
75 | #define LOGIN " login: " | |
6dbe3af9 KZ |
76 | |
77 | /* Some shorthands for control characters. */ | |
6dbe3af9 KZ |
78 | #define CTL(x) (x ^ 0100) /* Assumes ASCII dialect */ |
79 | #define CR CTL('M') /* carriage return */ | |
80 | #define NL CTL('J') /* line feed */ | |
81 | #define BS CTL('H') /* back space */ | |
82 | #define DEL CTL('?') /* delete */ | |
83 | ||
53d55042 | 84 | /* Defaults for line-editing etc. characters; you may want to change these. */ |
6dbe3af9 KZ |
85 | #define DEF_ERASE DEL /* default erase character */ |
86 | #define DEF_INTR CTL('C') /* default interrupt character */ | |
87 | #define DEF_QUIT CTL('\\') /* default quit char */ | |
88 | #define DEF_KILL CTL('U') /* default kill char */ | |
89 | #define DEF_EOF CTL('D') /* default EOF char */ | |
90 | #define DEF_EOL 0 | |
91 | #define DEF_SWITCH 0 /* default switch char */ | |
92 | ||
77337bfb | 93 | #ifndef MAXHOSTNAMELEN |
53d55042 SK |
94 | # ifdef HOST_NAME_MAX |
95 | # define MAXHOSTNAMELEN HOST_NAME_MAX | |
96 | # else | |
97 | # define MAXHOSTNAMELEN 64 | |
98 | # endif /* HOST_NAME_MAX */ | |
99 | #endif /* MAXHOSTNAMELEN */ | |
6dbe3af9 | 100 | |
b75c8134 KZ |
101 | /* |
102 | * When multiple baud rates are specified on the command line, the first one | |
103 | * we will try is the first one specified. | |
104 | */ | |
6dbe3af9 KZ |
105 | #define FIRST_SPEED 0 |
106 | ||
107 | /* Storage for command-line options. */ | |
53d55042 | 108 | #define MAX_SPEED 10 /* max. nr. of baud rates */ |
6dbe3af9 KZ |
109 | |
110 | struct options { | |
53d55042 SK |
111 | int flags; /* toggle switches, see below */ |
112 | int timeout; /* time-out period */ | |
113 | char *login; /* login program */ | |
114 | char *tty; /* name of tty */ | |
115 | char *initstring; /* modem init string */ | |
116 | char *issue; /* alternative issue file */ | |
117 | int numspeed; /* number of baud rates to try */ | |
118 | int speeds[MAX_SPEED]; /* baud rates to be tried */ | |
119 | int eightbits; /* assume 8bit-clean tty */ | |
6dbe3af9 KZ |
120 | }; |
121 | ||
53d55042 SK |
122 | #define F_PARSE (1<<0) /* process modem status messages */ |
123 | #define F_ISSUE (1<<1) /* display /etc/issue */ | |
124 | #define F_RTSCTS (1<<2) /* enable RTS/CTS flow control */ | |
125 | #define F_LOCAL (1<<3) /* force local */ | |
126 | #define F_INITSTRING (1<<4) /* initstring is set */ | |
127 | #define F_WAITCRLF (1<<5) /* wait for CR or LF */ | |
128 | #define F_CUSTISSUE (1<<6) /* give alternative issue file */ | |
129 | #define F_NOPROMPT (1<<7) /* do not ask for login name! */ | |
130 | #define F_LCUC (1<<8) /* support for *LCUC stty modes */ | |
131 | #define F_KEEPSPEED (1<<9) /* follow baud rate from kernel */ | |
132 | #define F_KEEPCFLAGS (1<<10) /* reuse c_cflags setup from kernel */ | |
6dbe3af9 KZ |
133 | |
134 | /* Storage for things detected while the login name was read. */ | |
6dbe3af9 | 135 | struct chardata { |
53d55042 SK |
136 | int erase; /* erase character */ |
137 | int kill; /* kill character */ | |
138 | int eol; /* end-of-line character */ | |
139 | int parity; /* what parity did we see */ | |
140 | int capslock; /* upper case without lower case */ | |
6dbe3af9 KZ |
141 | }; |
142 | ||
143 | /* Initial values for the above. */ | |
6dbe3af9 | 144 | struct chardata init_chardata = { |
53d55042 SK |
145 | DEF_ERASE, /* default erase character */ |
146 | DEF_KILL, /* default kill character */ | |
147 | 13, /* default eol char */ | |
148 | 0, /* space parity */ | |
149 | 0, /* no capslock */ | |
fd6b7a7f KZ |
150 | }; |
151 | ||
152 | struct Speedtab { | |
53d55042 SK |
153 | long speed; |
154 | int code; | |
fd6b7a7f KZ |
155 | }; |
156 | ||
157 | static struct Speedtab speedtab[] = { | |
53d55042 SK |
158 | {50, B50}, |
159 | {75, B75}, | |
160 | {110, B110}, | |
161 | {134, B134}, | |
162 | {150, B150}, | |
163 | {200, B200}, | |
164 | {300, B300}, | |
165 | {600, B600}, | |
166 | {1200, B1200}, | |
167 | {1800, B1800}, | |
168 | {2400, B2400}, | |
169 | {4800, B4800}, | |
170 | {9600, B9600}, | |
fd6b7a7f | 171 | #ifdef B19200 |
53d55042 | 172 | {19200, B19200}, |
fd6b7a7f KZ |
173 | #endif |
174 | #ifdef B38400 | |
53d55042 | 175 | {38400, B38400}, |
fd6b7a7f KZ |
176 | #endif |
177 | #ifdef EXTA | |
53d55042 | 178 | {19200, EXTA}, |
fd6b7a7f KZ |
179 | #endif |
180 | #ifdef EXTB | |
53d55042 | 181 | {38400, EXTB}, |
fd6b7a7f KZ |
182 | #endif |
183 | #ifdef B57600 | |
53d55042 | 184 | {57600, B57600}, |
fd6b7a7f KZ |
185 | #endif |
186 | #ifdef B115200 | |
53d55042 | 187 | {115200, B115200}, |
fd6b7a7f KZ |
188 | #endif |
189 | #ifdef B230400 | |
53d55042 | 190 | {230400, B230400}, |
fd6b7a7f | 191 | #endif |
53d55042 | 192 | {0, 0}, |
6dbe3af9 KZ |
193 | }; |
194 | ||
53d55042 SK |
195 | static void parse_args(int argc, char **argv, struct options *op); |
196 | static void parse_speeds(struct options *op, char *arg); | |
197 | static void update_utmp(char *line); | |
198 | static void open_tty(char *tty, struct termios *tp, int local); | |
199 | static void termio_init(struct options *op, struct termios *tp); | |
200 | static void auto_baud(struct termios *tp); | |
201 | static void do_prompt(struct options *op, struct termios *tp); | |
202 | static void next_speed(struct options *op, struct termios *tp); | |
203 | static char *get_logname(struct options *op, | |
204 | struct termios *tp, struct chardata *cp); | |
205 | static void termio_final(struct options *op, | |
206 | struct termios *tp, struct chardata *cp); | |
207 | static int caps_lock(char *s); | |
208 | static int bcode(char *s); | |
209 | static void usage(FILE * out); | |
210 | static void error(const char *, ...); | |
6dbe3af9 | 211 | |
eb63b9b8 KZ |
212 | /* Fake hostname for ut_host specified on command line. */ |
213 | char *fakehost = NULL; | |
214 | ||
6dbe3af9 KZ |
215 | #ifdef DEBUGGING |
216 | #define debug(s) fprintf(dbf,s); fflush(dbf) | |
217 | FILE *dbf; | |
218 | #else | |
53d55042 SK |
219 | #define debug(s) |
220 | #endif /* DEBUGGING */ | |
6dbe3af9 | 221 | |
53d55042 | 222 | int main(int argc, char **argv) |
6dbe3af9 | 223 | { |
53d55042 SK |
224 | char *logname = NULL; /* login name, given to /bin/login */ |
225 | struct chardata chardata; /* will be set by get_logname() */ | |
70bedfa1 KZ |
226 | struct termios termios; /* terminal mode bits */ |
227 | static struct options options = { | |
53d55042 SK |
228 | F_ISSUE, /* show /etc/issue (SYSV_STYLE) */ |
229 | 0, /* no timeout */ | |
230 | _PATH_LOGIN, /* default login program */ | |
231 | "tty1", /* default tty line */ | |
232 | "", /* modem init string */ | |
233 | ISSUE, /* default issue file */ | |
234 | 0, /* no baud rates known yet */ | |
70bedfa1 KZ |
235 | }; |
236 | ||
237 | setlocale(LC_ALL, ""); | |
238 | bindtextdomain(PACKAGE, LOCALEDIR); | |
239 | textdomain(PACKAGE); | |
b75c8134 | 240 | |
6dbe3af9 | 241 | #ifdef DEBUGGING |
fd6b7a7f | 242 | dbf = fopen("/dev/ttyp0", "w"); |
53d55042 SK |
243 | for (int i = 1; i < argc; i++) |
244 | debug(argv[i]); | |
245 | #endif /* DEBUGGING */ | |
6dbe3af9 | 246 | |
70bedfa1 | 247 | /* Parse command-line arguments. */ |
70bedfa1 | 248 | parse_args(argc, argv, &options); |
6dbe3af9 | 249 | |
fd6b7a7f | 250 | #ifdef __linux__ |
6dbe3af9 KZ |
251 | setsid(); |
252 | #endif | |
b75c8134 | 253 | |
70bedfa1 | 254 | /* Update the utmp file. */ |
6dbe3af9 | 255 | #ifdef SYSV_STYLE |
70bedfa1 | 256 | update_utmp(options.tty); |
6dbe3af9 KZ |
257 | #endif |
258 | ||
70bedfa1 | 259 | debug("calling open_tty\n"); |
53d55042 | 260 | |
70bedfa1 KZ |
261 | /* Open the tty as standard { input, output, error }. */ |
262 | open_tty(options.tty, &termios, options.flags & F_LOCAL); | |
263 | ||
264 | tcsetpgrp(STDIN_FILENO, getpid()); | |
265 | /* Initialize the termios settings (raw mode, eight-bit, blocking i/o). */ | |
266 | debug("calling termio_init\n"); | |
53d55042 | 267 | termio_init(&options, &termios); |
70bedfa1 | 268 | |
53d55042 | 269 | /* Write the modem init string and DO NOT flush the buffers. */ |
70bedfa1 KZ |
270 | if (options.flags & F_INITSTRING) { |
271 | debug("writing init string\n"); | |
53d55042 SK |
272 | ignore_result(write |
273 | (STDIN_FILENO, options.initstring, | |
274 | strlen(options.initstring))); | |
70bedfa1 KZ |
275 | } |
276 | ||
53d55042 SK |
277 | if (!(options.flags & F_LOCAL)) |
278 | /* Go to blocking write mode unless -L is specified. */ | |
70bedfa1 | 279 | fcntl(STDOUT_FILENO, F_SETFL, |
53d55042 | 280 | fcntl(STDOUT_FILENO, F_GETFL, 0) & ~O_NONBLOCK); |
70bedfa1 KZ |
281 | |
282 | /* Optionally detect the baud rate from the modem status message. */ | |
283 | debug("before autobaud\n"); | |
284 | if (options.flags & F_PARSE) | |
285 | auto_baud(&termios); | |
286 | ||
287 | /* Set the optional timer. */ | |
288 | if (options.timeout) | |
53d55042 | 289 | alarm((unsigned)options.timeout); |
70bedfa1 | 290 | |
53d55042 | 291 | /* Optionally wait for CR or LF before writing /etc/issue */ |
70bedfa1 KZ |
292 | if (options.flags & F_WAITCRLF) { |
293 | char ch; | |
294 | ||
295 | debug("waiting for cr-lf\n"); | |
53d55042 SK |
296 | while (read(STDIN_FILENO, &ch, 1) == 1) { |
297 | /* Strip "parity bit". */ | |
298 | ch &= 0x7f; | |
fd6b7a7f | 299 | #ifdef DEBUGGING |
70bedfa1 | 300 | fprintf(dbf, "read %c\n", ch); |
fd6b7a7f | 301 | #endif |
53d55042 SK |
302 | if (ch == '\n' || ch == '\r') |
303 | break; | |
70bedfa1 | 304 | } |
726f69e2 | 305 | } |
726f69e2 | 306 | |
70bedfa1 KZ |
307 | chardata = init_chardata; |
308 | if (!(options.flags & F_NOPROMPT)) { | |
309 | /* Read the login name. */ | |
310 | debug("reading login name\n"); | |
53d55042 SK |
311 | while ((logname = |
312 | get_logname(&options, &termios, &chardata)) == 0) | |
313 | next_speed(&options, &termios); | |
70bedfa1 | 314 | } |
6dbe3af9 | 315 | |
70bedfa1 | 316 | /* Disable timer. */ |
70bedfa1 | 317 | if (options.timeout) |
53d55042 | 318 | alarm(0); |
6dbe3af9 | 319 | |
70bedfa1 | 320 | /* Finalize the termios settings. */ |
70bedfa1 | 321 | termio_final(&options, &termios, &chardata); |
6dbe3af9 | 322 | |
70bedfa1 | 323 | /* Now the newline character should be properly written. */ |
53d55042 | 324 | ignore_result(write(STDOUT_FILENO, "\n", 1)); |
6dbe3af9 | 325 | |
70bedfa1 | 326 | /* Let the login program take care of password validation. */ |
53d55042 | 327 | execl(options.login, options.login, "--", logname, NULL); |
70bedfa1 | 328 | error(_("%s: can't exec %s: %m"), options.tty, options.login); |
6dbe3af9 KZ |
329 | } |
330 | ||
53d55042 SK |
331 | /* Parse command-line arguments. */ |
332 | void parse_args(int argc, char **argv, struct options *op) | |
6dbe3af9 | 333 | { |
53d55042 SK |
334 | extern char *optarg; |
335 | extern int optind; | |
336 | int c; | |
70bedfa1 KZ |
337 | |
338 | enum { | |
339 | VERSION_OPTION = CHAR_MAX + 1, | |
340 | HELP_OPTION | |
341 | }; | |
342 | static const struct option longopts[] = { | |
343 | { "8bits", no_argument, 0, '8' }, | |
344 | { "noreset", no_argument, 0, 'c' }, | |
345 | { "issue-file", required_argument, 0, 'f' }, | |
346 | { "flow-control", no_argument, 0, 'h' }, | |
347 | { "host", required_argument, 0, 'H' }, | |
348 | { "noissue", no_argument, 0, 'i' }, | |
349 | { "init-string", required_argument, 0, 'I' }, | |
350 | { "login-program", required_argument, 0, 'l' }, | |
351 | { "local-line", no_argument, 0, 'L' }, | |
352 | { "extract-baud", no_argument, 0, 'm' }, | |
353 | { "skip-login", no_argument, 0, 'n' }, | |
354 | { "keep-baud", no_argument, 0, 's' }, | |
355 | { "timeout", required_argument, 0, 't' }, | |
356 | { "detect-case", no_argument, 0, 'U' }, | |
357 | { "wait-cr", no_argument, 0, 'w' }, | |
358 | { "version", no_argument, 0, VERSION_OPTION }, | |
359 | { "help", no_argument, 0, HELP_OPTION }, | |
360 | { NULL, 0, 0, 0 } | |
361 | }; | |
362 | ||
53d55042 SK |
363 | while ((c = |
364 | getopt_long(argc, argv, "8cf:hH:iI:l:Lmnst:Uw", longopts, | |
365 | NULL)) != -1) { | |
70bedfa1 KZ |
366 | switch (c) { |
367 | case '8': | |
368 | op->eightbits = 1; | |
369 | break; | |
370 | case 'c': | |
371 | op->flags |= F_KEEPCFLAGS; | |
372 | break; | |
53d55042 | 373 | case 'f': |
70bedfa1 KZ |
374 | op->flags |= F_CUSTISSUE; |
375 | op->issue = optarg; | |
376 | break; | |
53d55042 | 377 | case 'h': |
70bedfa1 KZ |
378 | op->flags |= F_RTSCTS; |
379 | break; | |
53d55042 | 380 | case 'H': |
70bedfa1 KZ |
381 | fakehost = optarg; |
382 | break; | |
53d55042 | 383 | case 'i': |
70bedfa1 KZ |
384 | op->flags &= ~F_ISSUE; |
385 | break; | |
386 | case 'I': | |
53d55042 SK |
387 | /* |
388 | * FIXME: It would be better to use a separate | |
389 | * function for this task. | |
70bedfa1 | 390 | */ |
53d55042 SK |
391 | { |
392 | char ch, *p, *q; | |
393 | int i; | |
394 | ||
395 | op->initstring = xmalloc(strlen(optarg) + 1); | |
396 | ||
397 | /* | |
398 | * Copy optarg into op->initstring decoding \ddd octal | |
399 | * codes into chars. | |
400 | */ | |
401 | q = op->initstring; | |
402 | p = optarg; | |
403 | while (*p) { | |
404 | /* The \\ is converted to \ */ | |
70bedfa1 | 405 | if (*p == '\\') { |
70bedfa1 | 406 | p++; |
53d55042 SK |
407 | if (*p == '\\') { |
408 | ch = '\\'; | |
409 | p++; | |
410 | } else { | |
411 | /* Handle \000 - \177. */ | |
412 | ch = 0; | |
413 | for (i = 1; i <= 3; i++) { | |
414 | if (*p >= '0' && *p <= '7') { | |
415 | ch <<= 3; | |
416 | ch += *p - '0'; | |
417 | p++; | |
418 | } else { | |
419 | break; | |
420 | } | |
421 | } | |
70bedfa1 | 422 | } |
53d55042 SK |
423 | *q++ = ch; |
424 | } else | |
425 | *q++ = *p++; | |
70bedfa1 | 426 | } |
53d55042 SK |
427 | *q = '\0'; |
428 | op->flags |= F_INITSTRING; | |
429 | break; | |
726f69e2 | 430 | } |
70bedfa1 | 431 | case 'l': |
53d55042 | 432 | op->login = optarg; |
70bedfa1 | 433 | break; |
53d55042 | 434 | case 'L': |
70bedfa1 KZ |
435 | op->flags |= F_LOCAL; |
436 | break; | |
53d55042 | 437 | case 'm': |
70bedfa1 KZ |
438 | op->flags |= F_PARSE; |
439 | break; | |
440 | case 'n': | |
441 | op->flags |= F_NOPROMPT; | |
442 | break; | |
443 | case 's': | |
53d55042 | 444 | op->flags |= F_KEEPSPEED; |
70bedfa1 | 445 | break; |
53d55042 | 446 | case 't': |
70bedfa1 KZ |
447 | if ((op->timeout = atoi(optarg)) <= 0) |
448 | error(_("bad timeout value: %s"), optarg); | |
449 | break; | |
450 | case 'U': | |
451 | op->flags |= F_LCUC; | |
452 | break; | |
453 | case 'w': | |
454 | op->flags |= F_WAITCRLF; | |
455 | break; | |
456 | case VERSION_OPTION: | |
457 | printf(_("%s from %s\n"), program_invocation_short_name, | |
53d55042 | 458 | PACKAGE_STRING); |
70bedfa1 KZ |
459 | exit(EXIT_SUCCESS); |
460 | case HELP_OPTION: | |
461 | usage(stdout); | |
462 | default: | |
463 | usage(stderr); | |
726f69e2 | 464 | } |
6dbe3af9 | 465 | } |
70bedfa1 KZ |
466 | |
467 | debug("after getopt loop\n"); | |
468 | ||
53d55042 | 469 | if (argc < optind + 2) { |
70bedfa1 KZ |
470 | warnx(_("not enough arguments")); |
471 | usage(stderr); | |
472 | } | |
473 | ||
53d55042 SK |
474 | /* Accept both "baudrate tty" and "tty baudrate". */ |
475 | if ('0' <= argv[optind][0] && argv[optind][0] <= '9') { | |
476 | /* Assume BSD style speed. */ | |
477 | parse_speeds(op, argv[optind++]); | |
478 | op->tty = argv[optind]; | |
70bedfa1 | 479 | } else { |
53d55042 SK |
480 | op->tty = argv[optind++]; |
481 | parse_speeds(op, argv[optind]); | |
70bedfa1 KZ |
482 | } |
483 | ||
484 | optind++; | |
485 | if (argc > optind && argv[optind]) | |
53d55042 | 486 | setenv("TERM", argv[optind], 1); |
6dbe3af9 | 487 | |
66ee8158 | 488 | #ifdef DO_DEVFS_FIDDLING |
70bedfa1 | 489 | /* |
53d55042 | 490 | * Some devfs junk, following Goswin Brederlow: |
70bedfa1 KZ |
491 | * turn ttyS<n> into tts/<n> |
492 | * turn tty<n> into vc/<n> | |
53d55042 | 493 | * http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=72241 |
70bedfa1 KZ |
494 | */ |
495 | if (op->tty && strlen(op->tty) < 90) { | |
496 | char dev_name[100]; | |
497 | struct stat st; | |
498 | ||
499 | if (strncmp(op->tty, "ttyS", 4) == 0) { | |
500 | strcpy(dev_name, "/dev/"); | |
501 | strcat(dev_name, op->tty); | |
502 | if (stat(dev_name, &st) < 0) { | |
503 | strcpy(dev_name, "/dev/tts/"); | |
504 | strcat(dev_name, op->tty + 4); | |
505 | if (stat(dev_name, &st) == 0) | |
506 | op->tty = xstrdup(dev_name + 5); | |
507 | } | |
508 | } else if (strncmp(op->tty, "tty", 3) == 0) { | |
509 | strcpy(dev_name, "/dev/"); | |
510 | strncat(dev_name, op->tty, 90); | |
511 | if (stat(dev_name, &st) < 0) { | |
512 | strcpy(dev_name, "/dev/vc/"); | |
513 | strcat(dev_name, op->tty + 3); | |
514 | if (stat(dev_name, &st) == 0) | |
515 | op->tty = xstrdup(dev_name + 5); | |
516 | } | |
517 | } | |
518 | } | |
53d55042 | 519 | #endif /* DO_DEVFS_FIDDLING */ |
66ee8158 | 520 | |
70bedfa1 | 521 | debug("exiting parseargs\n"); |
6dbe3af9 KZ |
522 | } |
523 | ||
53d55042 SK |
524 | /* Parse alternate baud rates. */ |
525 | void parse_speeds(struct options *op, char *arg) | |
6dbe3af9 | 526 | { |
53d55042 | 527 | char *cp; |
6dbe3af9 | 528 | |
f021834e | 529 | debug("entered parse_speeds\n"); |
53d55042 | 530 | for (cp = strtok(arg, ","); cp != 0; cp = strtok((char *)0, ",")) { |
70bedfa1 KZ |
531 | if ((op->speeds[op->numspeed++] = bcode(cp)) <= 0) |
532 | error(_("bad speed: %s"), cp); | |
533 | if (op->numspeed >= MAX_SPEED) | |
534 | error(_("too many alternate speeds")); | |
535 | } | |
536 | debug("exiting parsespeeds\n"); | |
6dbe3af9 KZ |
537 | } |
538 | ||
539 | #ifdef SYSV_STYLE | |
540 | ||
53d55042 SK |
541 | /* Update our utmp entry. */ |
542 | void update_utmp(char *line) | |
6dbe3af9 | 543 | { |
53d55042 SK |
544 | struct utmp ut; |
545 | time_t t; | |
546 | int mypid = getpid(); | |
547 | struct utmp *utp; | |
70bedfa1 KZ |
548 | |
549 | /* | |
550 | * The utmp file holds miscellaneous information about things started by | |
551 | * /sbin/init and other system-related events. Our purpose is to update | |
552 | * the utmp entry for the current process, in particular the process type | |
553 | * and the tty line we are listening to. Return successfully only if the | |
554 | * utmp file can be opened for update, and if we are able to find our | |
555 | * entry in the utmp file. | |
556 | */ | |
70bedfa1 KZ |
557 | utmpname(_PATH_UTMP); |
558 | setutent(); | |
559 | ||
53d55042 SK |
560 | /* |
561 | * Find mypid in utmp. | |
562 | * | |
563 | * FIXME: Earlier (when was that?) code here tested only utp->ut_type != | |
564 | * INIT_PROCESS, so maybe the >= here should be >. | |
565 | * | |
566 | * FIXME: The present code is taken from login.c, so if this is changed, | |
567 | * maybe login has to be changed as well (is this true?). | |
568 | */ | |
70bedfa1 KZ |
569 | while ((utp = getutent())) |
570 | if (utp->ut_pid == mypid | |
571 | && utp->ut_type >= INIT_PROCESS | |
572 | && utp->ut_type <= DEAD_PROCESS) | |
573 | break; | |
574 | ||
575 | if (utp) { | |
576 | memcpy(&ut, utp, sizeof(ut)); | |
577 | } else { | |
53d55042 | 578 | /* Some inits do not initialize utmp. */ |
70bedfa1 KZ |
579 | memset(&ut, 0, sizeof(ut)); |
580 | strncpy(ut.ut_id, line + 3, sizeof(ut.ut_id)); | |
581 | } | |
582 | ||
583 | strncpy(ut.ut_user, "LOGIN", sizeof(ut.ut_user)); | |
584 | strncpy(ut.ut_line, line, sizeof(ut.ut_line)); | |
585 | if (fakehost) | |
586 | strncpy(ut.ut_host, fakehost, sizeof(ut.ut_host)); | |
587 | time(&t); | |
588 | ut.ut_time = t; | |
589 | ut.ut_type = LOGIN_PROCESS; | |
590 | ut.ut_pid = mypid; | |
591 | ||
592 | pututline(&ut); | |
593 | endutent(); | |
594 | ||
595 | { | |
48d7b13a | 596 | #ifdef HAVE_UPDWTMP |
70bedfa1 | 597 | updwtmp(_PATH_WTMP, &ut); |
5c36a0eb | 598 | #else |
70bedfa1 KZ |
599 | int ut_fd; |
600 | int lf; | |
601 | ||
53d55042 | 602 | if ((lf = open(_PATH_WTMPLOCK, O_CREAT | O_WRONLY, 0660)) >= 0) { |
70bedfa1 | 603 | flock(lf, LOCK_EX); |
53d55042 SK |
604 | if ((ut_fd = |
605 | open(_PATH_WTMP, O_APPEND | O_WRONLY)) >= 0) { | |
606 | ignore_result(write(ut_fd, &ut, sizeof(ut))); | |
70bedfa1 KZ |
607 | close(ut_fd); |
608 | } | |
609 | flock(lf, LOCK_UN); | |
610 | close(lf); | |
611 | } | |
53d55042 | 612 | #endif /* HAVE_UPDWTMP */ |
70bedfa1 | 613 | } |
6dbe3af9 KZ |
614 | } |
615 | ||
53d55042 | 616 | #endif /* SYSV_STYLE */ |
6dbe3af9 | 617 | |
53d55042 SK |
618 | /* Set up tty as stdin, stdout & stderr. */ |
619 | void open_tty(char *tty, struct termios *tp, int local) | |
6dbe3af9 | 620 | { |
53d55042 SK |
621 | /* Get rid of the present outputs. */ |
622 | close(STDOUT_FILENO); | |
623 | close(STDERR_FILENO); | |
624 | errno = 0; | |
6dbe3af9 | 625 | |
53d55042 SK |
626 | /* |
627 | * Set up new standard input, unless we are given an already opened | |
628 | * port. | |
629 | */ | |
70bedfa1 KZ |
630 | if (strcmp(tty, "-")) { |
631 | struct stat st; | |
6dbe3af9 | 632 | |
53d55042 | 633 | /* Sanity checks. */ |
70bedfa1 KZ |
634 | if (chdir("/dev")) |
635 | error(_("/dev: chdir() failed: %m")); | |
636 | if (stat(tty, &st) < 0) | |
637 | error("/dev/%s: %m", tty); | |
638 | if ((st.st_mode & S_IFMT) != S_IFCHR) | |
639 | error(_("/dev/%s: not a character device"), tty); | |
6dbe3af9 | 640 | |
70bedfa1 | 641 | /* Open the tty as standard input. */ |
53d55042 SK |
642 | close(STDIN_FILENO); |
643 | errno = 0; | |
6dbe3af9 | 644 | |
70bedfa1 | 645 | debug("open(2)\n"); |
53d55042 SK |
646 | if (open(tty, O_RDWR | O_NONBLOCK, 0) != 0) |
647 | error(_("/dev/%s: cannot open as standard input: %m"), | |
648 | tty); | |
70bedfa1 | 649 | } else { |
70bedfa1 KZ |
650 | /* |
651 | * Standard input should already be connected to an open port. Make | |
652 | * sure it is open for read/write. | |
653 | */ | |
70bedfa1 KZ |
654 | if ((fcntl(STDIN_FILENO, F_GETFL, 0) & O_RDWR) != O_RDWR) |
655 | error(_("%s: not open for read/write"), tty); | |
656 | } | |
657 | ||
658 | /* Set up standard output and standard error file descriptors. */ | |
659 | debug("duping\n"); | |
53d55042 | 660 | |
70bedfa1 KZ |
661 | /* set up stdout and stderr */ |
662 | if (dup(STDIN_FILENO) != 1 || dup(STDIN_FILENO) != 2) | |
53d55042 | 663 | error(_("%s: dup problem: %m"), tty); |
6dbe3af9 KZ |
664 | |
665 | /* | |
70bedfa1 KZ |
666 | * The following ioctl will fail if stdin is not a tty, but also when |
667 | * there is noise on the modem control lines. In the latter case, the | |
53d55042 SK |
668 | * common course of action is (1) fix your cables (2) give the modem |
669 | * more time to properly reset after hanging up. | |
670 | * | |
671 | * SunOS users can achieve (2) by patching the SunOS kernel variable | |
672 | * "zsadtrlow" to a larger value; 5 seconds seems to be a good value. | |
673 | * http://www.sunmanagers.org/archives/1993/0574.html | |
6dbe3af9 | 674 | */ |
70bedfa1 KZ |
675 | if (tcgetattr(STDIN_FILENO, tp) < 0) |
676 | error("%s: tcgetattr: %m", tty); | |
677 | ||
678 | /* | |
53d55042 SK |
679 | * Linux login(1) will change tty permissions. Use root owner and group |
680 | * with permission -rw------- for the period between getty and login. | |
70bedfa1 | 681 | */ |
53d55042 SK |
682 | ignore_result(chown(tty, 0, 0)); |
683 | ignore_result(chmod(tty, 0600)); | |
684 | errno = 0; | |
6dbe3af9 KZ |
685 | } |
686 | ||
53d55042 | 687 | /* Initialize termios settings. */ |
6dbe3af9 KZ |
688 | char gbuf[1024]; |
689 | char area[1024]; | |
690 | ||
53d55042 | 691 | void termio_init(struct options *op, struct termios *tp) |
6dbe3af9 | 692 | { |
70bedfa1 KZ |
693 | speed_t ispeed, ospeed; |
694 | ||
695 | if (op->flags & F_KEEPSPEED) { | |
53d55042 SK |
696 | /* Save the original setting. */ |
697 | ispeed = cfgetispeed(tp); | |
70bedfa1 | 698 | ospeed = cfgetospeed(tp); |
53d55042 | 699 | } else { |
70bedfa1 | 700 | ospeed = ispeed = op->speeds[FIRST_SPEED]; |
53d55042 | 701 | } |
70bedfa1 KZ |
702 | |
703 | /* | |
704 | * Initial termios settings: 8-bit characters, raw-mode, blocking i/o. | |
705 | * Special characters are set after we have read the login name; all | |
706 | * reads will be done in raw mode anyway. Errors will be dealt with | |
53d55042 | 707 | * later on. |
70bedfa1 | 708 | */ |
53d55042 SK |
709 | |
710 | /* Flush input and output queues, important for modems! */ | |
711 | tcflush(STDIN_FILENO, TCIOFLUSH); | |
70bedfa1 KZ |
712 | |
713 | tp->c_iflag = tp->c_lflag = tp->c_oflag = 0; | |
714 | ||
715 | if (!(op->flags & F_KEEPCFLAGS)) | |
716 | tp->c_cflag = CS8 | HUPCL | CREAD | (tp->c_cflag & CLOCAL); | |
717 | ||
53d55042 SK |
718 | /* |
719 | * Note that the speed is stored in the c_cflag termios field, so we have | |
70bedfa1 KZ |
720 | * set the speed always when the cflag se reseted. |
721 | */ | |
722 | cfsetispeed(tp, ispeed); | |
723 | cfsetospeed(tp, ospeed); | |
724 | ||
53d55042 | 725 | if (op->flags & F_LOCAL) |
70bedfa1 | 726 | tp->c_cflag |= CLOCAL; |
30e8b186 | 727 | #ifdef HAVE_STRUCT_TERMIOS_C_LINE |
70bedfa1 | 728 | tp->c_line = 0; |
1961482a | 729 | #endif |
70bedfa1 KZ |
730 | tp->c_cc[VMIN] = 1; |
731 | tp->c_cc[VTIME] = 0; | |
7eda085c | 732 | |
53d55042 | 733 | /* Optionally enable hardware flow control. */ |
7eda085c | 734 | #ifdef CRTSCTS |
70bedfa1 KZ |
735 | if (op->flags & F_RTSCTS) |
736 | tp->c_cflag |= CRTSCTS; | |
7eda085c KZ |
737 | #endif |
738 | ||
53d55042 | 739 | tcsetattr(STDIN_FILENO, TCSANOW, tp); |
fd6b7a7f | 740 | |
53d55042 SK |
741 | /* Go to blocking input even in local mode. */ |
742 | fcntl(STDIN_FILENO, F_SETFL, | |
743 | fcntl(STDIN_FILENO, F_GETFL, 0) & ~O_NONBLOCK); | |
fd6b7a7f | 744 | |
70bedfa1 | 745 | debug("term_io 2\n"); |
6dbe3af9 KZ |
746 | } |
747 | ||
53d55042 SK |
748 | /* Extract baud rate from modem status message. */ |
749 | void auto_baud(struct termios *tp) | |
6dbe3af9 | 750 | { |
53d55042 SK |
751 | int speed; |
752 | int vmin; | |
70bedfa1 | 753 | unsigned iflag; |
53d55042 SK |
754 | char buf[BUFSIZ]; |
755 | char *bp; | |
756 | int nread; | |
70bedfa1 KZ |
757 | |
758 | /* | |
759 | * This works only if the modem produces its status code AFTER raising | |
760 | * the DCD line, and if the computer is fast enough to set the proper | |
761 | * baud rate before the message has gone by. We expect a message of the | |
762 | * following format: | |
763 | * | |
764 | * <junk><number><junk> | |
765 | * | |
766 | * The number is interpreted as the baud rate of the incoming call. If the | |
767 | * modem does not tell us the baud rate within one second, we will keep | |
768 | * using the current baud rate. It is advisable to enable BREAK | |
769 | * processing (comma-separated list of baud rates) if the processing of | |
770 | * modem status messages is enabled. | |
771 | */ | |
772 | ||
773 | /* | |
774 | * Use 7-bit characters, don't block if input queue is empty. Errors will | |
53d55042 | 775 | * be dealt with later on. |
70bedfa1 | 776 | */ |
70bedfa1 | 777 | iflag = tp->c_iflag; |
53d55042 SK |
778 | /* Enable 8th-bit stripping. */ |
779 | tp->c_iflag |= ISTRIP; | |
70bedfa1 | 780 | vmin = tp->c_cc[VMIN]; |
53d55042 SK |
781 | /* Do not block when queue is empty. */ |
782 | tp->c_cc[VMIN] = 0; | |
70bedfa1 KZ |
783 | tcsetattr(STDIN_FILENO, TCSANOW, tp); |
784 | ||
785 | /* | |
786 | * Wait for a while, then read everything the modem has said so far and | |
787 | * try to extract the speed of the dial-in call. | |
788 | */ | |
53d55042 | 789 | sleep(1); |
70bedfa1 KZ |
790 | if ((nread = read(STDIN_FILENO, buf, sizeof(buf) - 1)) > 0) { |
791 | buf[nread] = '\0'; | |
53d55042 | 792 | for (bp = buf; bp < buf + nread; bp++) |
70bedfa1 KZ |
793 | if (isascii(*bp) && isdigit(*bp)) { |
794 | if ((speed = bcode(bp))) { | |
795 | cfsetispeed(tp, speed); | |
796 | cfsetospeed(tp, speed); | |
797 | } | |
798 | break; | |
799 | } | |
6dbe3af9 | 800 | } |
6dbe3af9 | 801 | |
53d55042 | 802 | /* Restore terminal settings. Errors will be dealt with later on. */ |
70bedfa1 KZ |
803 | tp->c_iflag = iflag; |
804 | tp->c_cc[VMIN] = vmin; | |
53d55042 | 805 | tcsetattr(STDIN_FILENO, TCSANOW, tp); |
6dbe3af9 KZ |
806 | } |
807 | ||
53d55042 SK |
808 | /* Show login prompt, optionally preceded by /etc/issue contents. */ |
809 | void do_prompt(struct options *op, struct termios *tp) | |
6dbe3af9 KZ |
810 | { |
811 | #ifdef ISSUE | |
53d55042 SK |
812 | FILE *fd; |
813 | int oflag; | |
814 | int c; | |
70bedfa1 | 815 | struct utsname uts; |
6dbe3af9 | 816 | |
53d55042 SK |
817 | uname(&uts); |
818 | #endif /* ISSUE */ | |
6dbe3af9 | 819 | |
53d55042 SK |
820 | /* Issue not in use, start with a new line. */ |
821 | ignore_result(write(STDOUT_FILENO, "\r\n", 2)); | |
822 | #ifdef ISSUE | |
70bedfa1 | 823 | if ((op->flags & F_ISSUE) && (fd = fopen(op->issue, "r"))) { |
53d55042 SK |
824 | /* Save current setting. */ |
825 | oflag = tp->c_oflag; | |
826 | /* Map new line in output to carriage return & new line. */ | |
827 | tp->c_oflag |= (ONLCR | OPOST); | |
828 | tcsetattr(STDIN_FILENO, TCSADRAIN, tp); | |
70bedfa1 KZ |
829 | |
830 | while ((c = getc(fd)) != EOF) { | |
831 | if (c == '\\') { | |
832 | c = getc(fd); | |
833 | ||
834 | switch (c) { | |
835 | case 's': | |
53d55042 | 836 | printf("%s", uts.sysname); |
70bedfa1 KZ |
837 | break; |
838 | case 'n': | |
53d55042 | 839 | printf("%s", uts.nodename); |
70bedfa1 KZ |
840 | break; |
841 | case 'r': | |
53d55042 | 842 | printf("%s", uts.release); |
70bedfa1 KZ |
843 | break; |
844 | case 'v': | |
53d55042 | 845 | printf("%s", uts.version); |
70bedfa1 KZ |
846 | break; |
847 | case 'm': | |
53d55042 | 848 | printf("%s", uts.machine); |
70bedfa1 KZ |
849 | break; |
850 | case 'o': | |
53d55042 SK |
851 | /* |
852 | * FIXME: It would be better to use a | |
853 | * separate function for this task. | |
854 | */ | |
855 | { | |
856 | char domainname[MAXHOSTNAMELEN + 1]; | |
48d7b13a | 857 | #ifdef HAVE_GETDOMAINNAME |
53d55042 SK |
858 | if (getdomainname(domainname, sizeof(domainname))) |
859 | #endif /* HAVE_GETDOMAINNAME */ | |
860 | strcpy(domainname, "unknown_domain"); | |
861 | domainname[sizeof(domainname) - 1] = '\0'; | |
862 | printf("%s", domainname); | |
863 | break; | |
864 | } | |
70bedfa1 | 865 | case 'O': |
53d55042 SK |
866 | /* |
867 | * FIXME: It would be better to use a | |
868 | * separate function for this task. | |
869 | */ | |
870 | { | |
871 | char *dom = "unknown_domain"; | |
872 | char host[MAXHOSTNAMELEN + 1]; | |
873 | struct addrinfo hints, *info = NULL; | |
874 | ||
875 | memset(&hints, 0, sizeof(hints)); | |
876 | hints.ai_flags = AI_CANONNAME; | |
877 | ||
878 | if (gethostname(host, sizeof(host)) | |
879 | || getaddrinfo(host, NULL, &hints, &info) | |
880 | || info == NULL) { | |
881 | fputs(dom, stdout); | |
882 | } else { | |
883 | char *canon; | |
884 | ||
885 | if (info->ai_canonname | |
886 | && (canon = | |
887 | strchr(info->ai_canonname, '.'))) { | |
888 | dom = canon + 1; | |
889 | } | |
890 | fputs(dom, stdout); | |
891 | freeaddrinfo(info); | |
892 | } | |
893 | break; | |
70bedfa1 | 894 | } |
70bedfa1 KZ |
895 | case 'd': |
896 | case 't': | |
53d55042 SK |
897 | /* |
898 | * FIXME: It would be better to use a | |
899 | * separate function for this task. | |
900 | */ | |
901 | { | |
902 | time_t now; | |
903 | struct tm *tm; | |
904 | ||
905 | time(&now); | |
906 | tm = localtime(&now); | |
907 | ||
908 | if (c == 'd') | |
909 | printf | |
910 | ("%s %s %d %d", | |
911 | nl_langinfo(ABDAY_1 + tm->tm_wday), | |
912 | nl_langinfo(ABMON_1 + tm->tm_mon), | |
913 | tm->tm_mday, | |
914 | /* FIXME: y2070 bug */ | |
915 | tm->tm_year < 70 ? | |
916 | tm->tm_year + 2000 : | |
917 | tm->tm_year + 1900); | |
918 | else | |
919 | printf("%02d:%02d:%02d", | |
920 | tm->tm_hour, tm->tm_min, tm->tm_sec); | |
921 | break; | |
922 | } | |
70bedfa1 | 923 | case 'l': |
53d55042 | 924 | printf("%s", op->tty); |
70bedfa1 KZ |
925 | break; |
926 | case 'b': | |
53d55042 SK |
927 | for (int i = 0; speedtab[i].speed; i++) |
928 | if (speedtab[i].code == cfgetispeed(tp)) | |
70bedfa1 | 929 | printf("%ld", speedtab[i].speed); |
70bedfa1 | 930 | break; |
53d55042 | 931 | break; |
70bedfa1 KZ |
932 | case 'u': |
933 | case 'U': | |
53d55042 SK |
934 | /* |
935 | * FIXME: It would be better to use a | |
936 | * separate function for this task. | |
937 | */ | |
938 | { | |
939 | int users = 0; | |
940 | struct utmp *ut; | |
941 | setutent(); | |
942 | while ((ut = getutent())) | |
943 | if (ut->ut_type == USER_PROCESS) | |
944 | users++; | |
945 | endutent(); | |
946 | printf("%d ", users); | |
947 | if (c == 'U') | |
948 | printf((users == 1) ? | |
949 | _("user") : _("users")); | |
950 | break; | |
951 | } | |
70bedfa1 | 952 | default: |
53d55042 | 953 | putchar(c); |
70bedfa1 KZ |
954 | } |
955 | } else | |
53d55042 | 956 | putchar(c); |
70bedfa1 KZ |
957 | } |
958 | fflush(stdout); | |
6dbe3af9 | 959 | |
53d55042 SK |
960 | /* Restore settings. */ |
961 | tp->c_oflag = oflag; | |
962 | /* Wait till output is gone. */ | |
963 | tcsetattr(STDIN_FILENO, TCSADRAIN, tp); | |
964 | fclose(fd); | |
6dbe3af9 | 965 | } |
53d55042 | 966 | #endif /* ISSUE */ |
70bedfa1 | 967 | { |
53d55042 | 968 | char hn[MAXHOSTNAMELEN + 1]; |
70bedfa1 | 969 | if (gethostname(hn, sizeof(hn)) == 0) |
53d55042 | 970 | ignore_result(write(STDIN_FILENO, hn, strlen(hn))); |
70bedfa1 | 971 | } |
53d55042 SK |
972 | /* Always show login prompt. */ |
973 | ignore_result(write(STDOUT_FILENO, LOGIN, sizeof(LOGIN) - 1)); | |
6dbe3af9 KZ |
974 | } |
975 | ||
53d55042 SK |
976 | /* Select next baud rate. */ |
977 | void next_speed(struct options *op, struct termios *tp) | |
6dbe3af9 | 978 | { |
70bedfa1 KZ |
979 | static int baud_index = -1; |
980 | ||
981 | if (baud_index == -1) | |
982 | /* | |
53d55042 | 983 | * If the F_KEEPSPEED flags is set then the FIRST_SPEED is not |
70bedfa1 KZ |
984 | * tested yet (see termio_init()). |
985 | */ | |
53d55042 SK |
986 | baud_index = |
987 | (op->flags & F_KEEPSPEED) ? FIRST_SPEED : 1 % op->numspeed; | |
70bedfa1 KZ |
988 | else |
989 | baud_index = (baud_index + 1) % op->numspeed; | |
990 | ||
991 | cfsetispeed(tp, op->speeds[baud_index]); | |
992 | cfsetospeed(tp, op->speeds[baud_index]); | |
53d55042 | 993 | tcsetattr(STDIN_FILENO, TCSANOW, tp); |
6dbe3af9 KZ |
994 | } |
995 | ||
53d55042 SK |
996 | /* Get user name, establish parity, speed, erase, kill & eol. */ |
997 | char *get_logname(struct options *op, struct termios *tp, struct chardata *cp) | |
6dbe3af9 | 998 | { |
70bedfa1 | 999 | static char logname[BUFSIZ]; |
53d55042 SK |
1000 | char *bp; |
1001 | char c; /* input character, full eight bits */ | |
1002 | char ascval; /* low 7 bits of input character */ | |
1003 | int bits; /* # of "1" bits per character */ | |
1004 | int mask; /* mask with 1 bit up */ | |
1005 | static char *erase[] = { /* backspace-space-backspace */ | |
1006 | "\010\040\010", /* space parity */ | |
1007 | "\010\040\010", /* odd parity */ | |
1008 | "\210\240\210", /* even parity */ | |
1009 | "\210\240\210", /* no parity */ | |
70bedfa1 KZ |
1010 | }; |
1011 | ||
1012 | /* Initialize kill, erase, parity etc. (also after switching speeds). */ | |
70bedfa1 KZ |
1013 | *cp = init_chardata; |
1014 | ||
53d55042 SK |
1015 | /* |
1016 | * Flush pending input (especially important after parsing or switching | |
1017 | * the baud rate). | |
1018 | */ | |
1019 | sleep(1); | |
1020 | tcflush(STDIN_FILENO, TCIFLUSH); | |
70bedfa1 KZ |
1021 | |
1022 | /* Prompt for and read a login name. */ | |
70bedfa1 KZ |
1023 | for (*logname = 0; *logname == 0; /* void */ ) { |
1024 | /* Write issue file and prompt, with "parity" bit == 0. */ | |
1025 | do_prompt(op, tp); | |
1026 | ||
53d55042 SK |
1027 | /* |
1028 | * Read name, watch for break, parity, erase, kill, | |
1029 | * end-of-line. | |
1030 | */ | |
70bedfa1 | 1031 | for (bp = logname, cp->eol = 0; cp->eol == 0; /* void */ ) { |
70bedfa1 KZ |
1032 | /* Do not report trivial EINTR/EIO errors. */ |
1033 | if (read(STDIN_FILENO, &c, 1) < 1) { | |
1034 | if (errno == EINTR || errno == EIO) | |
1035 | exit(EXIT_SUCCESS); | |
1036 | error(_("%s: read: %m"), op->tty); | |
1037 | } | |
1038 | /* Do BREAK handling elsewhere. */ | |
1039 | if ((c == 0) && op->numspeed > 1) | |
1040 | return EXIT_SUCCESS; | |
1041 | /* Do parity bit handling. */ | |
1042 | if (op->eightbits) { | |
1043 | ascval = c; | |
53d55042 SK |
1044 | } else if (c != (ascval = (c & 0177))) { |
1045 | /* Set "parity" bit on. */ | |
1046 | for (bits = 1, mask = 1; mask & 0177; | |
1047 | mask <<= 1) | |
70bedfa1 | 1048 | if (mask & ascval) |
53d55042 SK |
1049 | /* Count "1" bits. */ |
1050 | bits++; | |
70bedfa1 KZ |
1051 | cp->parity |= ((bits & 1) ? 1 : 2); |
1052 | } | |
1053 | /* Do erase, kill and end-of-line processing. */ | |
1054 | switch (ascval) { | |
1055 | case CR: | |
1056 | case NL: | |
53d55042 SK |
1057 | /* Terminate logname. */ |
1058 | *bp = 0; | |
1059 | /* Set end-of-line char. */ | |
1060 | cp->eol = ascval; | |
70bedfa1 KZ |
1061 | break; |
1062 | case BS: | |
1063 | case DEL: | |
1064 | case '#': | |
53d55042 SK |
1065 | /* Set erase character. */ |
1066 | cp->erase = ascval; | |
70bedfa1 | 1067 | if (bp > logname) { |
53d55042 SK |
1068 | ignore_result(write |
1069 | (STDIN_FILENO, | |
1070 | erase[cp->parity], 3)); | |
70bedfa1 KZ |
1071 | bp--; |
1072 | } | |
1073 | break; | |
1074 | case CTL('U'): | |
1075 | case '@': | |
53d55042 SK |
1076 | /* Set kill character. */ |
1077 | cp->kill = ascval; | |
70bedfa1 | 1078 | while (bp > logname) { |
53d55042 SK |
1079 | ignore_result(write |
1080 | (STDIN_FILENO, | |
1081 | erase[cp->parity], 3)); | |
70bedfa1 KZ |
1082 | bp--; |
1083 | } | |
1084 | break; | |
1085 | case CTL('D'): | |
1086 | exit(EXIT_SUCCESS); | |
1087 | default: | |
1088 | if (!isascii(ascval) || !isprint(ascval)) { | |
53d55042 | 1089 | /* Ignore garbage characters. */ ; |
70bedfa1 KZ |
1090 | } else if (bp - logname >= sizeof(logname) - 1) { |
1091 | error(_("%s: input overrun"), op->tty); | |
1092 | } else { | |
53d55042 SK |
1093 | /* Echo the character... */ |
1094 | ignore_result(write | |
1095 | (STDIN_FILENO, &c, 1)); | |
1096 | /* ...and store it. */ | |
1097 | *bp++ = ascval; | |
70bedfa1 KZ |
1098 | } |
1099 | break; | |
1100 | } | |
6dbe3af9 | 1101 | } |
6dbe3af9 | 1102 | } |
70bedfa1 | 1103 | /* Handle names with upper case and no lower case. */ |
53d55042 | 1104 | if ((op->flags & F_LCUC) && (cp->capslock = caps_lock(logname))) |
70bedfa1 KZ |
1105 | for (bp = logname; *bp; bp++) |
1106 | if (isupper(*bp)) | |
53d55042 | 1107 | *bp = tolower(*bp); |
70bedfa1 | 1108 | return logname; |
6dbe3af9 KZ |
1109 | } |
1110 | ||
53d55042 SK |
1111 | /* Set the final tty mode bits. */ |
1112 | void termio_final(struct options *op, struct termios *tp, struct chardata *cp) | |
6dbe3af9 | 1113 | { |
70bedfa1 | 1114 | /* General terminal-independent stuff. */ |
53d55042 SK |
1115 | |
1116 | /* 2-way flow control */ | |
1117 | tp->c_iflag |= IXON | IXOFF; | |
1118 | tp->c_lflag |= ICANON | ISIG | ECHO | ECHOE | ECHOK | ECHOKE; | |
1119 | /* no longer| ECHOCTL | ECHOPRT */ | |
70bedfa1 KZ |
1120 | tp->c_oflag |= OPOST; |
1121 | /* tp->c_cflag = 0; */ | |
53d55042 SK |
1122 | tp->c_cc[VINTR] = DEF_INTR; |
1123 | tp->c_cc[VQUIT] = DEF_QUIT; | |
1124 | tp->c_cc[VEOF] = DEF_EOF; | |
70bedfa1 | 1125 | tp->c_cc[VEOL] = DEF_EOL; |
fd6b7a7f | 1126 | #ifdef __linux__ |
53d55042 | 1127 | tp->c_cc[VSWTC] = DEF_SWITCH; |
1961482a | 1128 | #elif defined(VSWTCH) |
53d55042 SK |
1129 | tp->c_cc[VSWTCH] = DEF_SWITCH; |
1130 | #endif /* __linux__ */ | |
6dbe3af9 | 1131 | |
70bedfa1 KZ |
1132 | /* Account for special characters seen in input. */ |
1133 | if (cp->eol == CR) { | |
53d55042 SK |
1134 | tp->c_iflag |= ICRNL; |
1135 | tp->c_oflag |= ONLCR; | |
70bedfa1 | 1136 | } |
53d55042 SK |
1137 | tp->c_cc[VERASE] = cp->erase; |
1138 | tp->c_cc[VKILL] = cp->kill; | |
70bedfa1 KZ |
1139 | |
1140 | /* Account for the presence or absence of parity bits in input. */ | |
1141 | switch (cp->parity) { | |
53d55042 SK |
1142 | case 0: |
1143 | /* space (always 0) parity */ | |
1144 | break; | |
1145 | case 1: | |
1146 | /* odd parity */ | |
1147 | tp->c_cflag |= PARODD; | |
1148 | /* do not break */ | |
1149 | case 2: | |
1150 | /* even parity */ | |
1151 | tp->c_cflag |= PARENB; | |
1152 | tp->c_iflag |= INPCK | ISTRIP; | |
1153 | /* do not break */ | |
1154 | case (1 | 2): | |
1155 | /* no parity bit */ | |
1156 | tp->c_cflag &= ~CSIZE; | |
1157 | tp->c_cflag |= CS7; | |
1158 | break; | |
70bedfa1 KZ |
1159 | } |
1160 | /* Account for upper case without lower case. */ | |
1161 | if (cp->capslock) { | |
ee519041 | 1162 | #ifdef IUCLC |
70bedfa1 | 1163 | tp->c_iflag |= IUCLC; |
ee519041 | 1164 | #endif |
b75c8134 | 1165 | #ifdef XCASE |
70bedfa1 | 1166 | tp->c_lflag |= XCASE; |
1961482a | 1167 | #endif |
ee519041 | 1168 | #ifdef OLCUC |
70bedfa1 | 1169 | tp->c_oflag |= OLCUC; |
ee519041 | 1170 | #endif |
70bedfa1 | 1171 | } |
53d55042 | 1172 | /* Optionally enable hardware flow control. */ |
6dbe3af9 | 1173 | #ifdef CRTSCTS |
70bedfa1 KZ |
1174 | if (op->flags & F_RTSCTS) |
1175 | tp->c_cflag |= CRTSCTS; | |
6dbe3af9 KZ |
1176 | #endif |
1177 | ||
53d55042 | 1178 | /* Finally, make the new settings effective. */ |
70bedfa1 KZ |
1179 | if (tcsetattr(STDIN_FILENO, TCSANOW, tp) < 0) |
1180 | error("%s: tcsetattr: TCSANOW: %m", op->tty); | |
6dbe3af9 KZ |
1181 | } |
1182 | ||
53d55042 SK |
1183 | /* |
1184 | * String contains upper case without lower case. | |
1185 | * http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=52940 | |
1186 | * http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=156242 | |
1187 | */ | |
1188 | int caps_lock(char *s) | |
6dbe3af9 | 1189 | { |
53d55042 | 1190 | int capslock; |
70bedfa1 KZ |
1191 | |
1192 | for (capslock = 0; *s; s++) { | |
1193 | if (islower(*s)) | |
1194 | return EXIT_SUCCESS; | |
1195 | if (capslock == 0) | |
1196 | capslock = isupper(*s); | |
1197 | } | |
1198 | return capslock; | |
6dbe3af9 KZ |
1199 | } |
1200 | ||
53d55042 | 1201 | /* Convert speed string to speed code; return 0 on failure. */ |
70bedfa1 | 1202 | int bcode(s) |
53d55042 | 1203 | char *s; |
6dbe3af9 | 1204 | { |
70bedfa1 | 1205 | struct Speedtab *sp; |
53d55042 | 1206 | long speed = atol(s); |
6dbe3af9 | 1207 | |
70bedfa1 KZ |
1208 | for (sp = speedtab; sp->speed; sp++) |
1209 | if (sp->speed == speed) | |
1210 | return sp->code; | |
1211 | return 0; | |
6dbe3af9 KZ |
1212 | } |
1213 | ||
53d55042 | 1214 | void __attribute__ ((__noreturn__)) usage(FILE * out) |
6dbe3af9 | 1215 | { |
53d55042 SK |
1216 | fprintf(out, _("\nUsage:\n" |
1217 | " %1$s [options] line baud_rate,... [termtype]\n" | |
1218 | " %1$s [options] baud_rate,... line [termtype]\n"), | |
70bedfa1 KZ |
1219 | program_invocation_short_name); |
1220 | ||
53d55042 SK |
1221 | fprintf(out, _("\nOptions:\n" |
1222 | " -8, --8bits assume 8-bit tty\n" | |
1223 | " -c, --noreset do not reset control mode\n" | |
1224 | " -f, --issue-file FILE display issue file\n" | |
1225 | " -h, --flow-control enable hardware flow control\n" | |
1226 | " -H, --host HOSTNAME specify login host\n" | |
1227 | " -i, --noissue do not display issue file\n" | |
1228 | " -I, --init-string STRING set init string\n" | |
1229 | " -l, --login-program FILE specify login program\n" | |
1230 | " -L, --local-line force local line\n" | |
1231 | " -m, --extract-baud extract baud rate during connect\n" | |
1232 | " -n, --skip-login do not prompt for login\n" | |
1233 | " -s, --keep-baud try to keep baud rate after break\n" | |
1234 | " -t, --timeout NUMBER login process timeout\n" | |
1235 | " -U, --detect-case detect uppercase terminal\n" | |
1236 | " -w, --wait-cr wait carriage-return\n" | |
1237 | " --version output version information and exit\n" | |
1238 | " --help display this help and exit\n\n")); | |
70bedfa1 KZ |
1239 | |
1240 | exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS); | |
6dbe3af9 KZ |
1241 | } |
1242 | ||
53d55042 | 1243 | /* Report errors to console or syslog; only understands %s and %m. */ |
6dbe3af9 KZ |
1244 | #define str2cpy(b,s1,s2) strcat(strcpy(b,s1),s2) |
1245 | ||
53d55042 SK |
1246 | void error(const char *fmt, ...) |
1247 | { | |
70bedfa1 | 1248 | va_list ap; |
6dbe3af9 | 1249 | #ifndef USE_SYSLOG |
53d55042 | 1250 | int fd; |
6dbe3af9 | 1251 | #endif |
53d55042 SK |
1252 | char buf[BUFSIZ]; |
1253 | char *bp; | |
6dbe3af9 | 1254 | |
70bedfa1 KZ |
1255 | /* |
1256 | * If the diagnostic is reported via syslog(3), the process name is | |
1257 | * automatically prepended to the message. If we write directly to | |
1258 | * /dev/console, we must prepend the process name ourselves. | |
1259 | */ | |
6dbe3af9 | 1260 | #ifdef USE_SYSLOG |
70bedfa1 KZ |
1261 | buf[0] = '\0'; |
1262 | bp = buf; | |
6dbe3af9 | 1263 | #else |
53d55042 | 1264 | str2cpy(buf, program_invocation_short_name, ": "); |
70bedfa1 | 1265 | bp = buf + strlen(buf); |
53d55042 | 1266 | #endif /* USE_SYSLOG */ |
6dbe3af9 | 1267 | |
70bedfa1 KZ |
1268 | /* |
1269 | * %s expansion is done by hand. On a System V Release 2 system without | |
1270 | * shared libraries and without syslog(3), linking with the the stdio | |
53d55042 | 1271 | * library used to make the program three times as big... |
70bedfa1 KZ |
1272 | * |
1273 | * %m expansion is done here as well. Too bad syslog(3) does not have a | |
1274 | * vsprintf() like interface. | |
1275 | */ | |
70bedfa1 | 1276 | va_start(ap, fmt); |
53d55042 | 1277 | while (*fmt && bp < &buf[BUFSIZ - 1]) { |
70bedfa1 | 1278 | if (strncmp(fmt, "%s", 2) == 0) { |
53d55042 | 1279 | xstrncpy(bp, va_arg(ap, char *), &buf[BUFSIZ - 1] - bp); |
70bedfa1 KZ |
1280 | bp += strlen(bp); |
1281 | fmt += 2; | |
1282 | } else if (strncmp(fmt, "%m", 2) == 0) { | |
53d55042 | 1283 | xstrncpy(bp, strerror(errno), &buf[BUFSIZ - 1] - bp); |
70bedfa1 KZ |
1284 | bp += strlen(bp); |
1285 | fmt += 2; | |
1286 | } else { | |
1287 | *bp++ = *fmt++; | |
1288 | } | |
6dbe3af9 | 1289 | } |
70bedfa1 KZ |
1290 | *bp = 0; |
1291 | va_end(ap); | |
6dbe3af9 | 1292 | |
70bedfa1 KZ |
1293 | /* |
1294 | * Write the diagnostic directly to /dev/console if we do not use the | |
1295 | * syslog(3) facility. | |
1296 | */ | |
6dbe3af9 | 1297 | #ifdef USE_SYSLOG |
53d55042 SK |
1298 | openlog(program_invocation_short_name, LOG_PID, LOG_AUTHPRIV); |
1299 | syslog(LOG_ERR, "%s", buf); | |
70bedfa1 | 1300 | closelog(); |
6dbe3af9 | 1301 | #else |
70bedfa1 | 1302 | /* Terminate with CR-LF since the console mode is unknown. */ |
53d55042 | 1303 | strcat(bp, "\r\n"); |
70bedfa1 | 1304 | if ((fd = open("/dev/console", 1)) >= 0) { |
53d55042 SK |
1305 | ignore_result(write(fd, buf, strlen(buf))); |
1306 | close(fd); | |
70bedfa1 | 1307 | } |
53d55042 SK |
1308 | #endif /* USE_SYSLOG */ |
1309 | /* Be kind to init(8). */ | |
1310 | sleep(10); | |
70bedfa1 | 1311 | exit(EXIT_FAILURE); |
6dbe3af9 | 1312 | } |