]> git.ipfire.org Git - thirdparty/util-linux.git/blame - term-utils/agetty.c
agetty: more code cleanup
[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
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
110struct 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 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 153 long speed;
bde20e1b 154 speed_t 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);
bde20e1b
WF
208static speed_t bcode(char *s);
209static void usage(FILE * out) __attribute__((__noreturn__));
210static void log_err(const char *, ...) __attribute__((__noreturn__)) __attribute__((__format__(printf, 1, 2)));
211static void log_warn (const char *, ...) __attribute__((__format__(printf, 1, 2)));
6dbe3af9 212
eb63b9b8 213/* Fake hostname for ut_host specified on command line. */
bde20e1b 214static char *fakehost;
eb63b9b8 215
6dbe3af9
KZ
216#ifdef DEBUGGING
217#define debug(s) fprintf(dbf,s); fflush(dbf)
218FILE *dbf;
219#else
53d55042
SK
220#define debug(s)
221#endif /* DEBUGGING */
6dbe3af9 222
53d55042 223int 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 334static 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 526static 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 543static 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 620static 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 690static 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 748static 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 808static 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 976static 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 996static 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 1111static 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 1187static 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 1201static 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 1212static 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 1248static 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
1288static 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
1301static 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}