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