]> git.ipfire.org Git - thirdparty/util-linux.git/blame - term-utils/agetty.c
agetty.c: further scrubbing
[thirdparty/util-linux.git] / term-utils / agetty.c
CommitLineData
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
110struct 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 135struct 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 144struct 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
152struct Speedtab {
53d55042
SK
153 long speed;
154 int code;
fd6b7a7f
KZ
155};
156
157static 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
195static void parse_args(int argc, char **argv, struct options *op);
196static void parse_speeds(struct options *op, char *arg);
197static void update_utmp(char *line);
198static void open_tty(char *tty, struct termios *tp, int local);
199static void termio_init(struct options *op, struct termios *tp);
200static void auto_baud(struct termios *tp);
201static void do_prompt(struct options *op, struct termios *tp);
202static void next_speed(struct options *op, struct termios *tp);
203static char *get_logname(struct options *op,
204 struct termios *tp, struct chardata *cp);
205static void termio_final(struct options *op,
206 struct termios *tp, struct chardata *cp);
207static int caps_lock(char *s);
208static int bcode(char *s);
209static void usage(FILE * out);
210static void error(const char *, ...);
6dbe3af9 211
eb63b9b8
KZ
212/* Fake hostname for ut_host specified on command line. */
213char *fakehost = NULL;
214
6dbe3af9
KZ
215#ifdef DEBUGGING
216#define debug(s) fprintf(dbf,s); fflush(dbf)
217FILE *dbf;
218#else
53d55042
SK
219#define debug(s)
220#endif /* DEBUGGING */
6dbe3af9 221
53d55042 222int 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. */
332void 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. */
525void 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. */
542void 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. */
619void 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
688char gbuf[1024];
689char area[1024];
690
53d55042 691void 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. */
749void 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. */
809void 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. */
977void 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. */
997char *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. */
1112void 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 */
1188int 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 1202int bcode(s)
53d55042 1203char *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 1214void __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
1246void 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}