]> git.ipfire.org Git - thirdparty/util-linux.git/blame - term-utils/agetty.c
losetup: use loop-control to explicitly ask for device
[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>
1683f6bf
DWF
8 * Adopt the mingetty features for a better support
9 * of virtual consoles by Werner Fink <werner@suse.de>
b75c8134 10 *
53d55042 11 * This program is freely distributable.
b75c8134 12 */
6dbe3af9
KZ
13#include <stdio.h>
14#include <unistd.h>
15#include <stdlib.h>
2b6fc908 16#include <string.h>
1961482a 17#include <termios.h>
6dbe3af9
KZ
18#include <signal.h>
19#include <errno.h>
3aa6b68f 20#include <sys/ioctl.h>
6dbe3af9
KZ
21#include <sys/types.h>
22#include <sys/stat.h>
23#include <fcntl.h>
22853e4a 24#include <stdarg.h>
6dbe3af9
KZ
25#include <ctype.h>
26#include <utmp.h>
27#include <getopt.h>
22853e4a 28#include <time.h>
6dbe3af9 29#include <sys/file.h>
e61e66bd 30#include <sys/socket.h>
729def03 31#include <langinfo.h>
3aa6b68f 32#include <grp.h>
2b945eda
KZ
33#include <arpa/inet.h>
34#include <netdb.h>
35#include <ifaddrs.h>
df73ad46 36
8abcf290 37#include "strutils.h"
e12c9866 38#include "all-io.h"
7eda085c 39#include "nls.h"
e6590f06 40#include "pathnames.h"
e5b17b31 41#include "c.h"
1683f6bf 42#include "widechar.h"
879a7ab4 43#include "ttyutils.h"
6dbe3af9 44
fd6b7a7f 45#ifdef __linux__
0da025da 46# include <sys/kd.h>
53d55042
SK
47# include <sys/param.h>
48# define USE_SYSLOG
729def03
WF
49# ifndef DEFAULT_VCTERM
50# define DEFAULT_VCTERM "linux"
51# endif
52# ifndef DEFAULT_STERM
53# define DEFAULT_STERM "vt102"
54# endif
4585ec0b
ST
55#elif defined(__GNU__)
56# define USE_SYSLOG
57# ifndef DEFAULT_VCTERM
58# define DEFAULT_VCTERM "hurd"
59# endif
60# ifndef DEFAULT_STERM
61# define DEFAULT_STERM "vt102"
62# endif
729def03
WF
63#else
64# ifndef DEFAULT_VCTERM
65# define DEFAULT_VCTERM "vt100"
66# endif
67# ifndef DEFAULT_STERM
68# define DEFAULT_STERM "vt100"
69# endif
6dbe3af9
KZ
70#endif
71
53d55042 72/* If USE_SYSLOG is undefined all diagnostics go to /dev/console. */
6dbe3af9 73#ifdef USE_SYSLOG
53d55042 74# include <syslog.h>
6dbe3af9
KZ
75#endif
76
b75c8134
KZ
77/*
78 * Some heuristics to find out what environment we are in: if it is not
53d55042
SK
79 * System V, assume it is SunOS 4. The LOGIN_PROCESS is defined in System V
80 * utmp.h, which will select System V style getty.
b75c8134 81 */
53d55042
SK
82#ifdef LOGIN_PROCESS
83# define SYSV_STYLE
6dbe3af9
KZ
84#endif
85
b75c8134
KZ
86/*
87 * Things you may want to modify.
88 *
89 * If ISSUE is not defined, agetty will never display the contents of the
90 * /etc/issue file. You will not want to spit out large "issue" files at the
91 * wrong baud rate. Relevant for System V only.
92 *
93 * You may disagree with the default line-editing etc. characters defined
94 * below. Note, however, that DEL cannot be used for interrupt generation
95 * and for line editing at the same time.
96 */
6dbe3af9 97
53d55042 98/* Displayed before the login prompt. */
6dbe3af9 99#ifdef SYSV_STYLE
96cc7b0b 100# define ISSUE _PATH_ISSUE
53d55042 101# include <sys/utsname.h>
6dbe3af9
KZ
102#endif
103
53d55042 104/* Login prompt. */
e85281a8 105#define LOGIN "login: "
9aeb66dc 106#define LOGIN_ARGV_MAX 16 /* Numbers of args for login */
6dbe3af9 107
b75c8134
KZ
108/*
109 * When multiple baud rates are specified on the command line, the first one
110 * we will try is the first one specified.
111 */
6dbe3af9
KZ
112#define FIRST_SPEED 0
113
114/* Storage for command-line options. */
53d55042 115#define MAX_SPEED 10 /* max. nr. of baud rates */
6dbe3af9
KZ
116
117struct options {
bde20e1b
WF
118 int flags; /* toggle switches, see below */
119 int timeout; /* time-out period */
eb8e1f9f 120 char *autolog; /* login the user automatically */
e85281a8
DWF
121 char *chdir; /* Chdir before the login */
122 char *chroot; /* Chroot before the login */
bde20e1b 123 char *login; /* login program */
eb8e1f9f 124 char *logopt; /* options for login program */
bde20e1b 125 char *tty; /* name of tty */
729def03
WF
126 char *vcline; /* line of virtual console */
127 char *term; /* terminal type */
bde20e1b
WF
128 char *initstring; /* modem init string */
129 char *issue; /* alternative issue file */
cb872ac9
KZ
130 char *erasechars; /* string with erase chars */
131 char *killchars; /* string with kill chars */
e85281a8
DWF
132 int delay; /* Sleep seconds before prompt */
133 int nice; /* Run login with this priority */
bde20e1b 134 int numspeed; /* number of baud rates to try */
ef264c83 135 int clocal; /* CLOCAL_MODE_* */
bde20e1b 136 speed_t speeds[MAX_SPEED]; /* baud rates to be tried */
6dbe3af9
KZ
137};
138
ef264c83
KZ
139enum {
140 CLOCAL_MODE_AUTO = 0,
141 CLOCAL_MODE_ALWAYS,
142 CLOCAL_MODE_NEVER
143};
144
53d55042
SK
145#define F_PARSE (1<<0) /* process modem status messages */
146#define F_ISSUE (1<<1) /* display /etc/issue */
147#define F_RTSCTS (1<<2) /* enable RTS/CTS flow control */
ef264c83 148
53d55042
SK
149#define F_INITSTRING (1<<4) /* initstring is set */
150#define F_WAITCRLF (1<<5) /* wait for CR or LF */
151#define F_CUSTISSUE (1<<6) /* give alternative issue file */
152#define F_NOPROMPT (1<<7) /* do not ask for login name! */
153#define F_LCUC (1<<8) /* support for *LCUC stty modes */
154#define F_KEEPSPEED (1<<9) /* follow baud rate from kernel */
155#define F_KEEPCFLAGS (1<<10) /* reuse c_cflags setup from kernel */
bde20e1b 156#define F_EIGHTBITS (1<<11) /* Assume 8bit-clean tty */
3aa6b68f
WF
157#define F_VCONSOLE (1<<12) /* This is a virtual console */
158#define F_HANGUP (1<<13) /* Do call vhangup(2) */
0da025da 159#define F_UTF8 (1<<14) /* We can do UTF8 */
eb8e1f9f 160#define F_LOGINPAUSE (1<<15) /* Wait for any key before dropping login prompt */
e85281a8
DWF
161#define F_NOCLEAR (1<<16) /* Do not clear the screen before prompting */
162#define F_NONL (1<<17) /* No newline before issue */
163#define F_NOHOSTNAME (1<<18) /* Do not show the hostname */
164#define F_LONGHNAME (1<<19) /* Show Full qualified hostname */
36601b23 165#define F_NOHINTS (1<<20) /* Don't print hints */
01095ae3 166#define F_REMOTE (1<<21) /* Add '-h fakehost' to login(1) command line */
0da025da
WF
167
168#define serial_tty_option(opt, flag) \
169 (((opt)->flags & (F_VCONSOLE|(flag))) == (flag))
6dbe3af9 170
fd6b7a7f 171struct Speedtab {
53d55042 172 long speed;
bde20e1b 173 speed_t code;
fd6b7a7f
KZ
174};
175
1a204cd2 176static const struct Speedtab speedtab[] = {
53d55042
SK
177 {50, B50},
178 {75, B75},
179 {110, B110},
180 {134, B134},
181 {150, B150},
182 {200, B200},
183 {300, B300},
184 {600, B600},
185 {1200, B1200},
186 {1800, B1800},
187 {2400, B2400},
188 {4800, B4800},
189 {9600, B9600},
fd6b7a7f 190#ifdef B19200
53d55042 191 {19200, B19200},
fd6b7a7f
KZ
192#endif
193#ifdef B38400
53d55042 194 {38400, B38400},
fd6b7a7f
KZ
195#endif
196#ifdef EXTA
53d55042 197 {19200, EXTA},
fd6b7a7f
KZ
198#endif
199#ifdef EXTB
53d55042 200 {38400, EXTB},
fd6b7a7f
KZ
201#endif
202#ifdef B57600
53d55042 203 {57600, B57600},
fd6b7a7f
KZ
204#endif
205#ifdef B115200
53d55042 206 {115200, B115200},
fd6b7a7f
KZ
207#endif
208#ifdef B230400
53d55042 209 {230400, B230400},
fd6b7a7f 210#endif
53d55042 211 {0, 0},
6dbe3af9
KZ
212};
213
729def03 214static void init_special_char(char* arg, struct options *op);
53d55042
SK
215static void parse_args(int argc, char **argv, struct options *op);
216static void parse_speeds(struct options *op, char *arg);
729def03
WF
217static void update_utmp(struct options *op);
218static void open_tty(char *tty, struct termios *tp, struct options *op);
53d55042 219static void termio_init(struct options *op, struct termios *tp);
0da025da 220static void reset_vc (const struct options *op, struct termios *tp);
53d55042 221static void auto_baud(struct termios *tp);
2b945eda
KZ
222static void output_special_char (unsigned char c, struct options *op,
223 struct termios *tp, FILE *fp);
53d55042
SK
224static void do_prompt(struct options *op, struct termios *tp);
225static void next_speed(struct options *op, struct termios *tp);
226static char *get_logname(struct options *op,
227 struct termios *tp, struct chardata *cp);
228static void termio_final(struct options *op,
229 struct termios *tp, struct chardata *cp);
230static int caps_lock(char *s);
bde20e1b
WF
231static speed_t bcode(char *s);
232static void usage(FILE * out) __attribute__((__noreturn__));
763d7a87
KZ
233static void log_err(const char *, ...) __attribute__((__noreturn__))
234 __attribute__((__format__(printf, 1, 2)));
235static void log_warn (const char *, ...)
236 __attribute__((__format__(printf, 1, 2)));
a694957a 237static ssize_t append(char *dest, size_t len, const char *sep, const char *src);
9aeb66dc
KZ
238static void check_username (const char* nm);
239static void login_options_to_argv(char *argv[], int *argc, char *str, char *username);
6dbe3af9 240
eb63b9b8 241/* Fake hostname for ut_host specified on command line. */
bde20e1b 242static char *fakehost;
eb63b9b8 243
6dbe3af9 244#ifdef DEBUGGING
1aba8336
SK
245#ifndef
246# define DEBUG_OUTPUT "/dev/ttyp0"
247#endif
783b08fc 248#define debug(s) do { fprintf(dbf,s); fflush(dbf); } while (0)
6dbe3af9
KZ
249FILE *dbf;
250#else
efcf26f4
KZ
251#define debug(s) do { ; } while (0)
252#endif
6dbe3af9 253
53d55042 254int main(int argc, char **argv)
6dbe3af9 255{
9aeb66dc 256 char *username = NULL; /* login name, given to /bin/login */
eb8e1f9f
WF
257 struct chardata chardata; /* will be set by get_logname() */
258 struct termios termios; /* terminal mode bits */
1a204cd2 259 struct options options = {
eb8e1f9f
WF
260 .flags = F_ISSUE, /* show /etc/issue (SYSV_STYLE) */
261 .login = _PATH_LOGIN, /* default login program */
eb8e1f9f 262 .tty = "tty1", /* default tty line */
5676f365 263 .issue = ISSUE /* default issue file */
70bedfa1 264 };
9aeb66dc
KZ
265 char *login_argv[LOGIN_ARGV_MAX + 1];
266 int login_argc = 0;
3aa6b68f
WF
267 struct sigaction sa, sa_hup, sa_quit, sa_int;
268 sigset_t set;
70bedfa1
KZ
269
270 setlocale(LC_ALL, "");
271 bindtextdomain(PACKAGE, LOCALEDIR);
272 textdomain(PACKAGE);
b75c8134 273
3aa6b68f
WF
274 /* In case vhangup(2) has to called */
275 sa.sa_handler = SIG_IGN;
276 sa.sa_flags = SA_RESTART;
277 sigemptyset (&sa.sa_mask);
278 sigaction(SIGHUP, &sa, &sa_hup);
279 sigaction(SIGQUIT, &sa, &sa_quit);
280 sigaction(SIGINT, &sa, &sa_int);
281
6dbe3af9 282#ifdef DEBUGGING
1aba8336 283 dbf = fopen(DEBUG_OUTPUT, "w");
53d55042
SK
284 for (int i = 1; i < argc; i++)
285 debug(argv[i]);
286#endif /* DEBUGGING */
6dbe3af9 287
70bedfa1 288 /* Parse command-line arguments. */
70bedfa1 289 parse_args(argc, argv, &options);
6dbe3af9 290
9aeb66dc
KZ
291 login_argv[login_argc++] = options.login; /* set login program name */
292
70bedfa1 293 /* Update the utmp file. */
6dbe3af9 294#ifdef SYSV_STYLE
729def03 295 update_utmp(&options);
6dbe3af9 296#endif
e85281a8
DWF
297 if (options.delay)
298 sleep(options.delay);
299
70bedfa1 300 debug("calling open_tty\n");
53d55042 301
70bedfa1 302 /* Open the tty as standard { input, output, error }. */
729def03 303 open_tty(options.tty, &termios, &options);
70bedfa1 304
3aa6b68f
WF
305 /* Unmask SIGHUP if inherited */
306 sigemptyset(&set);
307 sigaddset(&set, SIGHUP);
308 sigprocmask(SIG_UNBLOCK, &set, NULL);
309 sigaction(SIGHUP, &sa_hup, NULL);
310
70bedfa1
KZ
311 tcsetpgrp(STDIN_FILENO, getpid());
312 /* Initialize the termios settings (raw mode, eight-bit, blocking i/o). */
313 debug("calling termio_init\n");
53d55042 314 termio_init(&options, &termios);
70bedfa1 315
53d55042 316 /* Write the modem init string and DO NOT flush the buffers. */
eb8e1f9f
WF
317 if (serial_tty_option(&options, F_INITSTRING) &&
318 options.initstring && *options.initstring != '\0') {
70bedfa1 319 debug("writing init string\n");
729def03 320 write_all(STDOUT_FILENO, options.initstring,
3aa6b68f 321 strlen(options.initstring));
70bedfa1
KZ
322 }
323
714cff30
KZ
324 if (options.flags & F_VCONSOLE || options.clocal != CLOCAL_MODE_ALWAYS)
325 /* Go to blocking mode unless -L is specified, this change
326 * affects stdout, stdin and stderr as all the file descriptors
327 * are created by dup(). */
70bedfa1 328 fcntl(STDOUT_FILENO, F_SETFL,
53d55042 329 fcntl(STDOUT_FILENO, F_GETFL, 0) & ~O_NONBLOCK);
70bedfa1
KZ
330
331 /* Optionally detect the baud rate from the modem status message. */
332 debug("before autobaud\n");
0da025da 333 if (serial_tty_option(&options, F_PARSE))
70bedfa1
KZ
334 auto_baud(&termios);
335
336 /* Set the optional timer. */
337 if (options.timeout)
763d7a87 338 alarm((unsigned) options.timeout);
70bedfa1 339
53d55042 340 /* Optionally wait for CR or LF before writing /etc/issue */
0da025da 341 if (serial_tty_option(&options, F_WAITCRLF)) {
70bedfa1
KZ
342 char ch;
343
344 debug("waiting for cr-lf\n");
53d55042
SK
345 while (read(STDIN_FILENO, &ch, 1) == 1) {
346 /* Strip "parity bit". */
347 ch &= 0x7f;
fd6b7a7f 348#ifdef DEBUGGING
70bedfa1 349 fprintf(dbf, "read %c\n", ch);
fd6b7a7f 350#endif
53d55042
SK
351 if (ch == '\n' || ch == '\r')
352 break;
70bedfa1 353 }
726f69e2 354 }
726f69e2 355
f5664477 356 INIT_CHARDATA(&chardata);
933956cb
KZ
357
358 if (options.autolog) {
359 debug("doing auto login\n");
360 username = options.autolog;
361 }
362
1683f6bf 363 if ((options.flags & F_NOPROMPT) == 0) {
eb8e1f9f 364 if (options.autolog) {
933956cb 365 /* Autologin prompt */
eb8e1f9f 366 do_prompt(&options, &termios);
8c219bf4 367 printf(_("%s%s (automatic login)\n"), LOGIN, options.autolog);
eb8e1f9f
WF
368 } else {
369 /* Read the login name. */
370 debug("reading login name\n");
9aeb66dc 371 while ((username =
f8bd089b 372 get_logname(&options, &termios, &chardata)) == NULL)
eb8e1f9f
WF
373 if ((options.flags & F_VCONSOLE) == 0)
374 next_speed(&options, &termios);
375 }
70bedfa1 376 }
6dbe3af9 377
70bedfa1 378 /* Disable timer. */
70bedfa1 379 if (options.timeout)
53d55042 380 alarm(0);
6dbe3af9 381
0da025da
WF
382 if ((options.flags & F_VCONSOLE) == 0) {
383 /* Finalize the termios settings. */
384 termio_final(&options, &termios, &chardata);
6dbe3af9 385
0da025da
WF
386 /* Now the newline character should be properly written. */
387 write_all(STDOUT_FILENO, "\r\n", 2);
388 }
6dbe3af9 389
763d7a87
KZ
390 sigaction(SIGQUIT, &sa_quit, NULL);
391 sigaction(SIGINT, &sa_int, NULL);
3aa6b68f 392
9aeb66dc
KZ
393 if (username)
394 check_username(username);
395
396 if (options.logopt) {
397 /*
398 * The --login-options completely overwrites the default
399 * way how agetty composes login(1) command line.
400 */
401 login_options_to_argv(login_argv, &login_argc,
402 options.logopt, username);
01095ae3
KZ
403 } else {
404 if (fakehost && (options.flags & F_REMOTE)) {
405 login_argv[login_argc++] = "-h";
406 login_argv[login_argc++] = fakehost;
407 }
408 if (username) {
409 if (options.autolog)
410 login_argv[login_argc++] = "-f";
411 else
412 login_argv[login_argc++] = "--";
413 login_argv[login_argc++] = username;
414 }
9aeb66dc 415 }
eb8e1f9f 416
9aeb66dc 417 login_argv[login_argc] = NULL; /* last login argv */
eb8e1f9f 418
e85281a8
DWF
419 if (options.chroot) {
420 if (chroot(options.chroot) < 0)
421 log_err(_("%s: can't change root directory %s: %m"),
422 options.tty, options.chroot);
423 }
424 if (options.chdir) {
425 if (chdir(options.chdir) < 0)
426 log_err(_("%s: can't change working directory %s: %m"),
427 options.tty, options.chdir);
428 }
429 if (options.nice) {
430 if (nice(options.nice) < 0)
431 log_warn(_("%s: can't change process priority: %m"),
432 options.tty);
433 }
434
1aba8336
SK
435#ifdef DEBUGGING
436 fprintf(dbf, "read %c\n", ch);
437 if (close_stream(dbf) != 0)
438 log_err("write failed: %s", DEBUG_OUTPUT);
439#endif
440
70bedfa1 441 /* Let the login program take care of password validation. */
9aeb66dc
KZ
442 execv(options.login, login_argv);
443 log_err(_("%s: can't exec %s: %m"), options.tty, login_argv[0]);
444}
445
446/*
447 * Returns : @str if \u not found
448 * : @username if @str equal to "\u"
449 * : newly allocated string if \u mixed with something other
450 */
451static char *replace_u(char *str, char *username)
452{
453 char *entry = NULL, *p = str;
454 size_t usz = username ? strlen(username) : 0;
455
456 while (*p) {
457 size_t sz;
458 char *tp, *old = entry;
459
460 if (memcmp(p, "\\u", 2)) {
461 p++;
462 continue; /* no \u */
463 }
464 sz = strlen(str);
465
3797cf25 466 if (p == str && sz == 2) {
9aeb66dc 467 /* 'str' contains only '\u' */
3797cf25 468 free(old);
9aeb66dc 469 return username;
3797cf25 470 }
9aeb66dc
KZ
471
472 tp = entry = malloc(sz + usz);
473 if (!tp)
474 log_err(_("failed to allocate memory: %m"));
475
476 if (p != str) {
477 /* copy chars befor \u */
478 memcpy(tp, str, p - str);
479 tp += p - str;
480 }
481 if (usz) {
482 /* copy username */
483 memcpy(tp, username, usz);
484 tp += usz;
485 }
486 if (*(p + 2))
487 /* copy chars after \u + \0 */
488 memcpy(tp, p + 2, sz - (p - str) - 1);
489 else
490 *tp = '\0';
491
492 p = tp;
493 str = entry;
494 free(old);
495 }
496
497 return entry ? entry : str;
498}
499
500static void login_options_to_argv(char *argv[], int *argc,
501 char *str, char *username)
502{
503 char *p;
504 int i = *argc;
505
506 while (str && isspace(*str))
507 str++;
508 p = str;
509
510 while (p && *p && i < LOGIN_ARGV_MAX) {
511 if (isspace(*p)) {
512 *p = '\0';
513 while (isspace(*++p))
514 ;
515 if (*p) {
516 argv[i++] = replace_u(str, username);
517 str = p;
518 }
519 } else
520 p++;
521 }
522 if (str && *str && i < LOGIN_ARGV_MAX)
523 argv[i++] = replace_u(str, username);
524 *argc = i;
6dbe3af9
KZ
525}
526
53d55042 527/* Parse command-line arguments. */
bde20e1b 528static void parse_args(int argc, char **argv, struct options *op)
6dbe3af9 529{
53d55042 530 int c;
70bedfa1
KZ
531
532 enum {
533 VERSION_OPTION = CHAR_MAX + 1,
36601b23 534 NOHINTS_OPTION,
e85281a8
DWF
535 NOHOSTNAME_OPTION,
536 LONGHOSTNAME_OPTION,
cb872ac9
KZ
537 HELP_OPTION,
538 ERASE_CHARS_OPTION,
539 KILL_CHARS_OPTION,
70bedfa1 540 };
3aa6b68f 541 const struct option longopts[] = {
70bedfa1 542 { "8bits", no_argument, 0, '8' },
eb8e1f9f 543 { "autologin", required_argument, 0, 'a' },
70bedfa1 544 { "noreset", no_argument, 0, 'c' },
e85281a8
DWF
545 { "chdir", required_argument, 0, 'C' },
546 { "delay", required_argument, 0, 'd' },
01095ae3 547 { "remote", no_argument, 0, 'E' },
70bedfa1
KZ
548 { "issue-file", required_argument, 0, 'f' },
549 { "flow-control", no_argument, 0, 'h' },
550 { "host", required_argument, 0, 'H' },
551 { "noissue", no_argument, 0, 'i' },
552 { "init-string", required_argument, 0, 'I' },
e85281a8 553 { "noclear", no_argument, 0, 'J' },
70bedfa1 554 { "login-program", required_argument, 0, 'l' },
ef264c83 555 { "local-line", optional_argument, 0, 'L' },
70bedfa1
KZ
556 { "extract-baud", no_argument, 0, 'm' },
557 { "skip-login", no_argument, 0, 'n' },
e85281a8 558 { "nonewline", no_argument, 0, 'N' },
eb8e1f9f 559 { "login-options", required_argument, 0, 'o' },
3fc62fd3 560 { "login-pause", no_argument, 0, 'p' },
e85281a8
DWF
561 { "nice", required_argument, 0, 'P' },
562 { "chroot", required_argument, 0, 'r' },
3aa6b68f 563 { "hangup", no_argument, 0, 'R' },
70bedfa1
KZ
564 { "keep-baud", no_argument, 0, 's' },
565 { "timeout", required_argument, 0, 't' },
566 { "detect-case", no_argument, 0, 'U' },
567 { "wait-cr", no_argument, 0, 'w' },
36601b23 568 { "nohints", no_argument, 0, NOHINTS_OPTION },
3fc62fd3 569 { "nohostname", no_argument, 0, NOHOSTNAME_OPTION },
e85281a8 570 { "long-hostname", no_argument, 0, LONGHOSTNAME_OPTION },
70bedfa1
KZ
571 { "version", no_argument, 0, VERSION_OPTION },
572 { "help", no_argument, 0, HELP_OPTION },
cb872ac9
KZ
573 { "erase-chars", required_argument, 0, ERASE_CHARS_OPTION },
574 { "kill-chars", required_argument, 0, KILL_CHARS_OPTION },
70bedfa1
KZ
575 { NULL, 0, 0, 0 }
576 };
577
763d7a87 578 while ((c = getopt_long(argc, argv,
01095ae3 579 "8a:cC:d:Ef:hH:iI:Jl:LmnNo:pP:r:Rst:Uw", longopts,
53d55042 580 NULL)) != -1) {
70bedfa1
KZ
581 switch (c) {
582 case '8':
bde20e1b 583 op->flags |= F_EIGHTBITS;
70bedfa1 584 break;
eb8e1f9f
WF
585 case 'a':
586 op->autolog = optarg;
587 break;
70bedfa1
KZ
588 case 'c':
589 op->flags |= F_KEEPCFLAGS;
590 break;
e85281a8
DWF
591 case 'C':
592 op->chdir = optarg;
593 break;
594 case 'd':
595 op->delay = atoi(optarg);
596 break;
01095ae3
KZ
597 case 'E':
598 op->flags |= F_REMOTE;
599 break;
53d55042 600 case 'f':
70bedfa1
KZ
601 op->flags |= F_CUSTISSUE;
602 op->issue = optarg;
603 break;
53d55042 604 case 'h':
70bedfa1
KZ
605 op->flags |= F_RTSCTS;
606 break;
53d55042 607 case 'H':
70bedfa1
KZ
608 fakehost = optarg;
609 break;
53d55042 610 case 'i':
70bedfa1
KZ
611 op->flags &= ~F_ISSUE;
612 break;
613 case 'I':
729def03
WF
614 init_special_char(optarg, op);
615 op->flags |= F_INITSTRING;
616 break;
e85281a8
DWF
617 case 'J':
618 op->flags |= F_NOCLEAR;
619 break;
70bedfa1 620 case 'l':
53d55042 621 op->login = optarg;
70bedfa1 622 break;
53d55042 623 case 'L':
ef264c83
KZ
624 /* -L and -L=always have the same meaning */
625 op->clocal = CLOCAL_MODE_ALWAYS;
626 if (optarg) {
627 if (strcmp(optarg, "=always") == 0)
628 op->clocal = CLOCAL_MODE_ALWAYS;
629 else if (strcmp(optarg, "=never") == 0)
630 op->clocal = CLOCAL_MODE_NEVER;
631 else if (strcmp(optarg, "=auto") == 0)
632 op->clocal = CLOCAL_MODE_AUTO;
633 else
634 log_err(_("unssuported --local-line mode argument"));
635 }
70bedfa1 636 break;
53d55042 637 case 'm':
70bedfa1
KZ
638 op->flags |= F_PARSE;
639 break;
640 case 'n':
641 op->flags |= F_NOPROMPT;
642 break;
eb8e1f9f
WF
643 case 'o':
644 op->logopt = optarg;
645 break;
646 case 'p':
647 op->flags |= F_LOGINPAUSE;
648 break;
e85281a8
DWF
649 case 'P':
650 op->nice = atoi(optarg);
651 break;
652 case 'r':
653 op->chroot = optarg;
654 break;
3aa6b68f
WF
655 case 'R':
656 op->flags |= F_HANGUP;
657 break;
70bedfa1 658 case 's':
53d55042 659 op->flags |= F_KEEPSPEED;
70bedfa1 660 break;
53d55042 661 case 't':
70bedfa1 662 if ((op->timeout = atoi(optarg)) <= 0)
bde20e1b 663 log_err(_("bad timeout value: %s"), optarg);
70bedfa1
KZ
664 break;
665 case 'U':
666 op->flags |= F_LCUC;
667 break;
668 case 'w':
669 op->flags |= F_WAITCRLF;
670 break;
36601b23
KZ
671 case NOHINTS_OPTION:
672 op->flags |= F_NOHINTS;
673 break;
e85281a8
DWF
674 case NOHOSTNAME_OPTION:
675 op->flags |= F_NOHOSTNAME;
676 break;
677 case LONGHOSTNAME_OPTION:
678 op->flags |= F_LONGHNAME;
679 break;
cb872ac9
KZ
680 case ERASE_CHARS_OPTION:
681 op->erasechars = optarg;
682 break;
683 case KILL_CHARS_OPTION:
684 op->killchars = optarg;
685 break;
70bedfa1
KZ
686 case VERSION_OPTION:
687 printf(_("%s from %s\n"), program_invocation_short_name,
53d55042 688 PACKAGE_STRING);
70bedfa1
KZ
689 exit(EXIT_SUCCESS);
690 case HELP_OPTION:
691 usage(stdout);
692 default:
693 usage(stderr);
726f69e2 694 }
6dbe3af9 695 }
70bedfa1
KZ
696
697 debug("after getopt loop\n");
698
729def03 699 if (argc < optind + 1) {
bde20e1b 700 log_warn(_("not enough arguments"));
70bedfa1
KZ
701 usage(stderr);
702 }
703
729def03 704 /* Accept "tty", "baudrate tty", and "tty baudrate". */
53d55042
SK
705 if ('0' <= argv[optind][0] && argv[optind][0] <= '9') {
706 /* Assume BSD style speed. */
707 parse_speeds(op, argv[optind++]);
729def03
WF
708 if (argc < optind + 1) {
709 warn(_("not enough arguments"));
710 usage(stderr);
711 }
712 op->tty = argv[optind++];
70bedfa1 713 } else {
53d55042 714 op->tty = argv[optind++];
729def03
WF
715 if (argc > optind) {
716 char *v = argv[optind++];
717 if ('0' <= *v && *v <= '9')
718 parse_speeds(op, v);
719 else
720 op->speeds[op->numspeed++] = bcode("9600");
721 }
70bedfa1
KZ
722 }
723
729def03
WF
724 /* On virtual console remember the line which is used for */
725 if (strncmp(op->tty, "tty", 3) == 0 &&
726 strspn(op->tty + 3, "0123456789") == strlen(op->tty+3))
727 op->vcline = op->tty+3;
728
70bedfa1 729 if (argc > optind && argv[optind])
729def03 730 op->term = argv[optind];
6dbe3af9 731
66ee8158 732#ifdef DO_DEVFS_FIDDLING
70bedfa1 733 /*
53d55042 734 * Some devfs junk, following Goswin Brederlow:
70bedfa1
KZ
735 * turn ttyS<n> into tts/<n>
736 * turn tty<n> into vc/<n>
53d55042 737 * http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=72241
70bedfa1
KZ
738 */
739 if (op->tty && strlen(op->tty) < 90) {
740 char dev_name[100];
741 struct stat st;
742
743 if (strncmp(op->tty, "ttyS", 4) == 0) {
744 strcpy(dev_name, "/dev/");
745 strcat(dev_name, op->tty);
746 if (stat(dev_name, &st) < 0) {
747 strcpy(dev_name, "/dev/tts/");
748 strcat(dev_name, op->tty + 4);
ca8e91a4
KZ
749 if (stat(dev_name, &st) == 0) {
750 op->tty = strdup(dev_name + 5);
751 if (!op->tty)
752 log_err(_("failed to allocate memory: %m"));
753 }
70bedfa1
KZ
754 }
755 } else if (strncmp(op->tty, "tty", 3) == 0) {
756 strcpy(dev_name, "/dev/");
757 strncat(dev_name, op->tty, 90);
758 if (stat(dev_name, &st) < 0) {
759 strcpy(dev_name, "/dev/vc/");
760 strcat(dev_name, op->tty + 3);
ca8e91a4
KZ
761 if (stat(dev_name, &st) == 0) {
762 op->tty = strdup(dev_name + 5);
763 if (!op->tty)
764 log_err(_("failed to allocate memory: %m"));
765 }
70bedfa1
KZ
766 }
767 }
768 }
53d55042 769#endif /* DO_DEVFS_FIDDLING */
66ee8158 770
70bedfa1 771 debug("exiting parseargs\n");
6dbe3af9
KZ
772}
773
53d55042 774/* Parse alternate baud rates. */
bde20e1b 775static void parse_speeds(struct options *op, char *arg)
6dbe3af9 776{
53d55042 777 char *cp;
6dbe3af9 778
f021834e 779 debug("entered parse_speeds\n");
f8bd089b 780 for (cp = strtok(arg, ","); cp != NULL; cp = strtok((char *)0, ",")) {
70bedfa1 781 if ((op->speeds[op->numspeed++] = bcode(cp)) <= 0)
bde20e1b 782 log_err(_("bad speed: %s"), cp);
70bedfa1 783 if (op->numspeed >= MAX_SPEED)
bde20e1b 784 log_err(_("too many alternate speeds"));
70bedfa1
KZ
785 }
786 debug("exiting parsespeeds\n");
6dbe3af9
KZ
787}
788
789#ifdef SYSV_STYLE
790
53d55042 791/* Update our utmp entry. */
729def03 792static void update_utmp(struct options *op)
6dbe3af9 793{
53d55042
SK
794 struct utmp ut;
795 time_t t;
729def03
WF
796 pid_t pid = getpid();
797 pid_t sid = getsid(0);
798 char *vcline = op->vcline;
799 char *line = op->tty;
53d55042 800 struct utmp *utp;
70bedfa1
KZ
801
802 /*
803 * The utmp file holds miscellaneous information about things started by
804 * /sbin/init and other system-related events. Our purpose is to update
805 * the utmp entry for the current process, in particular the process type
806 * and the tty line we are listening to. Return successfully only if the
807 * utmp file can be opened for update, and if we are able to find our
808 * entry in the utmp file.
809 */
70bedfa1
KZ
810 utmpname(_PATH_UTMP);
811 setutent();
812
53d55042 813 /*
729def03 814 * Find my pid in utmp.
53d55042
SK
815 *
816 * FIXME: Earlier (when was that?) code here tested only utp->ut_type !=
817 * INIT_PROCESS, so maybe the >= here should be >.
818 *
819 * FIXME: The present code is taken from login.c, so if this is changed,
820 * maybe login has to be changed as well (is this true?).
821 */
70bedfa1 822 while ((utp = getutent()))
729def03 823 if (utp->ut_pid == pid
70bedfa1
KZ
824 && utp->ut_type >= INIT_PROCESS
825 && utp->ut_type <= DEAD_PROCESS)
826 break;
827
828 if (utp) {
829 memcpy(&ut, utp, sizeof(ut));
830 } else {
53d55042 831 /* Some inits do not initialize utmp. */
70bedfa1 832 memset(&ut, 0, sizeof(ut));
729def03
WF
833 if (vcline && *vcline)
834 /* Standard virtual console devices */
763d7a87 835 strncpy(ut.ut_id, vcline, sizeof(ut.ut_id));
729def03
WF
836 else {
837 size_t len = strlen(line);
838 char * ptr;
763d7a87
KZ
839 if (len >= sizeof(ut.ut_id))
840 ptr = line + len - sizeof(ut.ut_id);
729def03
WF
841 else
842 ptr = line;
763d7a87 843 strncpy(ut.ut_id, ptr, sizeof(ut.ut_id));
729def03 844 }
70bedfa1
KZ
845 }
846
847 strncpy(ut.ut_user, "LOGIN", sizeof(ut.ut_user));
848 strncpy(ut.ut_line, line, sizeof(ut.ut_line));
849 if (fakehost)
850 strncpy(ut.ut_host, fakehost, sizeof(ut.ut_host));
851 time(&t);
852 ut.ut_time = t;
853 ut.ut_type = LOGIN_PROCESS;
729def03
WF
854 ut.ut_pid = pid;
855 ut.ut_session = sid;
70bedfa1
KZ
856
857 pututline(&ut);
858 endutent();
859
860 {
48d7b13a 861#ifdef HAVE_UPDWTMP
70bedfa1 862 updwtmp(_PATH_WTMP, &ut);
5c36a0eb 863#else
70bedfa1
KZ
864 int ut_fd;
865 int lf;
866
53d55042 867 if ((lf = open(_PATH_WTMPLOCK, O_CREAT | O_WRONLY, 0660)) >= 0) {
70bedfa1 868 flock(lf, LOCK_EX);
53d55042
SK
869 if ((ut_fd =
870 open(_PATH_WTMP, O_APPEND | O_WRONLY)) >= 0) {
729def03 871 write_all(ut_fd, &ut, sizeof(ut));
70bedfa1
KZ
872 close(ut_fd);
873 }
874 flock(lf, LOCK_UN);
875 close(lf);
876 }
53d55042 877#endif /* HAVE_UPDWTMP */
70bedfa1 878 }
6dbe3af9
KZ
879}
880
53d55042 881#endif /* SYSV_STYLE */
6dbe3af9 882
53d55042 883/* Set up tty as stdin, stdout & stderr. */
729def03 884static void open_tty(char *tty, struct termios *tp, struct options *op)
6dbe3af9 885{
3aa6b68f 886 const pid_t pid = getpid();
783b08fc 887 int serial, closed = 0;
6dbe3af9 888
3aa6b68f
WF
889 /* Set up new standard input, unless we are given an already opened port. */
890
891 if (strcmp(tty, "-") != 0) {
892 char buf[PATH_MAX+1];
893 struct group *gr = NULL;
70bedfa1 894 struct stat st;
3aa6b68f
WF
895 int fd, len;
896 pid_t tid;
897 gid_t gid = 0;
898
899 /* Use tty group if available */
900 if ((gr = getgrnam("tty")))
901 gid = gr->gr_gid;
902
763d7a87
KZ
903 if (((len = snprintf(buf, sizeof(buf), "/dev/%s", tty)) >=
904 (int)sizeof(buf)) || (len < 0))
3aa6b68f
WF
905 log_err(_("/dev/%s: cannot open as standard input: %m"), tty);
906
907 /*
908 * There is always a race between this reset and the call to
909 * vhangup() that s.o. can use to get access to your tty.
910 * Linux login(1) will change tty permissions. Use root owner and group
911 * with permission -rw------- for the period between getty and login.
912 */
91984e93 913 if (chown(buf, 0, gid) || chmod(buf, (gid ? 0620 : 0600))) {
3aa6b68f
WF
914 if (errno == EROFS)
915 log_warn("%s: %m", buf);
916 else
917 log_err("%s: %m", buf);
918 }
6dbe3af9 919
3aa6b68f
WF
920 /* Open the tty as standard input. */
921 if ((fd = open(buf, O_RDWR|O_NOCTTY|O_NONBLOCK, 0)) < 0)
922 log_err(_("/dev/%s: cannot open as standard input: %m"), tty);
923
924 /* Sanity checks... */
763d7a87 925 if (!isatty(fd))
3aa6b68f
WF
926 log_err(_("/dev/%s: not a character device"), tty);
927 if (fstat(fd, &st) < 0)
763d7a87 928 log_err("%s: %m", buf);
70bedfa1 929 if ((st.st_mode & S_IFMT) != S_IFCHR)
bde20e1b 930 log_err(_("/dev/%s: not a character device"), tty);
6dbe3af9 931
3aa6b68f 932 if (((tid = tcgetsid(fd)) < 0) || (pid != tid)) {
763d7a87 933 if (ioctl(fd, TIOCSCTTY, 1) == -1)
8c219bf4 934 log_warn(_("/dev/%s: cannot get controlling tty: %m"), tty);
3aa6b68f
WF
935 }
936
783b08fc
KZ
937 close(STDIN_FILENO);
938 errno = 0;
939
3aa6b68f 940 if (op->flags & F_HANGUP) {
783b08fc
KZ
941
942 if (ioctl(fd, TIOCNOTTY))
943 debug("TIOCNOTTY ioctl failed\n");
944
3aa6b68f 945 /*
783b08fc
KZ
946 * Let's close all file decriptors before vhangup
947 * https://lkml.org/lkml/2012/6/5/145
3aa6b68f 948 */
783b08fc
KZ
949 close(fd);
950 close(STDOUT_FILENO);
951 close(STDERR_FILENO);
952 errno = 0;
953 closed = 1;
954
3aa6b68f 955 if (vhangup())
8c219bf4 956 log_err(_("/dev/%s: vhangup() failed: %m"), tty);
783b08fc
KZ
957 } else
958 close(fd);
6dbe3af9 959
70bedfa1 960 debug("open(2)\n");
3aa6b68f
WF
961 if (open(buf, O_RDWR|O_NOCTTY|O_NONBLOCK, 0) != 0)
962 log_err(_("/dev/%s: cannot open as standard input: %m"), tty);
783b08fc 963
3aa6b68f 964 if (((tid = tcgetsid(STDIN_FILENO)) < 0) || (pid != tid)) {
763d7a87 965 if (ioctl(STDIN_FILENO, TIOCSCTTY, 1) == -1)
8c219bf4 966 log_warn(_("/dev/%s: cannot get controlling tty: %m"), tty);
3aa6b68f
WF
967 }
968
70bedfa1 969 } else {
3aa6b68f 970
70bedfa1
KZ
971 /*
972 * Standard input should already be connected to an open port. Make
973 * sure it is open for read/write.
974 */
3aa6b68f 975
70bedfa1 976 if ((fcntl(STDIN_FILENO, F_GETFL, 0) & O_RDWR) != O_RDWR)
bde20e1b 977 log_err(_("%s: not open for read/write"), tty);
3aa6b68f 978
70bedfa1
KZ
979 }
980
3aa6b68f 981 if (tcsetpgrp(STDIN_FILENO, pid))
8c219bf4 982 log_warn(_("/dev/%s: cannot set process group: %m"), tty);
3aa6b68f
WF
983
984 /* Get rid of the present outputs. */
783b08fc
KZ
985 if (!closed) {
986 close(STDOUT_FILENO);
987 close(STDERR_FILENO);
988 errno = 0;
989 }
3aa6b68f 990
70bedfa1
KZ
991 /* Set up standard output and standard error file descriptors. */
992 debug("duping\n");
53d55042 993
70bedfa1
KZ
994 /* set up stdout and stderr */
995 if (dup(STDIN_FILENO) != 1 || dup(STDIN_FILENO) != 2)
bde20e1b 996 log_err(_("%s: dup problem: %m"), tty);
6dbe3af9 997
3aa6b68f
WF
998 /* make stdio unbuffered for slow modem lines */
999 setvbuf(stdout, NULL, _IONBF, 0);
1000
6dbe3af9 1001 /*
70bedfa1
KZ
1002 * The following ioctl will fail if stdin is not a tty, but also when
1003 * there is noise on the modem control lines. In the latter case, the
53d55042
SK
1004 * common course of action is (1) fix your cables (2) give the modem
1005 * more time to properly reset after hanging up.
1006 *
1007 * SunOS users can achieve (2) by patching the SunOS kernel variable
1008 * "zsadtrlow" to a larger value; 5 seconds seems to be a good value.
1009 * http://www.sunmanagers.org/archives/1993/0574.html
6dbe3af9 1010 */
bde20e1b 1011 memset(tp, 0, sizeof(struct termios));
70bedfa1 1012 if (tcgetattr(STDIN_FILENO, tp) < 0)
8c219bf4 1013 log_err(_("%s: failed to get terminal attributes: %m"), tty);
70bedfa1
KZ
1014
1015 /*
3aa6b68f
WF
1016 * Detect if this is a virtual console or serial/modem line.
1017 * In case of a virtual console the ioctl TIOCMGET fails and
1018 * the error number will be set to EINVAL.
70bedfa1 1019 */
89aed1c9 1020 if (ioctl(STDIN_FILENO, TIOCMGET, &serial) < 0 && (errno == EINVAL)) {
3aa6b68f
WF
1021 op->flags |= F_VCONSOLE;
1022 if (!op->term)
1023 op->term = DEFAULT_VCTERM;
1024 } else if (!op->term)
1025 op->term = DEFAULT_STERM;
729def03
WF
1026
1027 setenv("TERM", op->term, 1);
6dbe3af9
KZ
1028}
1029
53d55042 1030/* Initialize termios settings. */
bde20e1b 1031static void termio_init(struct options *op, struct termios *tp)
6dbe3af9 1032{
70bedfa1 1033 speed_t ispeed, ospeed;
1683f6bf 1034 struct winsize ws;
70bedfa1 1035
0da025da
WF
1036 if (op->flags & F_VCONSOLE) {
1037#if defined(IUTF8) && defined(KDGKBMODE)
1038 int mode;
1039
1040 /* Detect mode of current keyboard setup, e.g. for UTF-8 */
1041 if (ioctl(STDIN_FILENO, KDGKBMODE, &mode) < 0)
1042 mode = K_RAW;
1043 switch(mode) {
1044 case K_UNICODE:
b0198a11 1045 setlocale(LC_CTYPE, "C.UTF-8");
0da025da
WF
1046 op->flags |= F_UTF8;
1047 break;
1048 case K_RAW:
1049 case K_MEDIUMRAW:
1050 case K_XLATE:
1051 default:
1052 setlocale(LC_CTYPE, "POSIX");
1053 op->flags &= ~F_UTF8;
1054 break;
1055 }
1056#else
1057 setlocale(LC_CTYPE, "POSIX");
1058 op->flags &= ~F_UTF8;
1059#endif
1060 reset_vc(op, tp);
1061
1062 if ((tp->c_cflag & (CS8|PARODD|PARENB)) == CS8)
1063 op->flags |= F_EIGHTBITS;
1064
e85281a8
DWF
1065 if ((op->flags & F_NOCLEAR) == 0) {
1066 /*
1067 * Do not write a full reset (ESC c) because this destroys
1068 * the unicode mode again if the terminal was in unicode
1069 * mode. Also it clears the CONSOLE_MAGIC features which
1070 * are required for some languages/console-fonts.
1071 * Just put the cursor to the home position (ESC [ H),
1072 * erase everything below the cursor (ESC [ J), and set the
1073 * scrolling region to the full window (ESC [ r)
1074 */
1075 write_all(STDOUT_FILENO, "\033[r\033[H\033[J", 9);
1076 }
0da025da
WF
1077 return;
1078 }
1079
70bedfa1 1080 if (op->flags & F_KEEPSPEED) {
53d55042
SK
1081 /* Save the original setting. */
1082 ispeed = cfgetispeed(tp);
70bedfa1 1083 ospeed = cfgetospeed(tp);
1683f6bf
DWF
1084
1085 if (!ispeed) ispeed = TTYDEF_SPEED;
1086 if (!ospeed) ospeed = TTYDEF_SPEED;
1087
53d55042 1088 } else {
70bedfa1 1089 ospeed = ispeed = op->speeds[FIRST_SPEED];
53d55042 1090 }
70bedfa1
KZ
1091
1092 /*
1093 * Initial termios settings: 8-bit characters, raw-mode, blocking i/o.
1094 * Special characters are set after we have read the login name; all
1095 * reads will be done in raw mode anyway. Errors will be dealt with
53d55042 1096 * later on.
70bedfa1 1097 */
53d55042
SK
1098
1099 /* Flush input and output queues, important for modems! */
1100 tcflush(STDIN_FILENO, TCIOFLUSH);
70bedfa1 1101
8408647d
WF
1102#ifdef IUTF8
1103 tp->c_iflag = tp->c_iflag & IUTF8;
7478fce0
ST
1104 if (tp->c_iflag & IUTF8)
1105 op->flags |= F_UTF8;
8408647d
WF
1106#else
1107 tp->c_iflag = 0;
1108#endif
9c62a232
DJ
1109 tp->c_lflag = 0;
1110 tp->c_oflag &= OPOST | ONLCR;
70bedfa1 1111
1683f6bf 1112 if ((op->flags & F_KEEPCFLAGS) == 0)
70bedfa1
KZ
1113 tp->c_cflag = CS8 | HUPCL | CREAD | (tp->c_cflag & CLOCAL);
1114
53d55042
SK
1115 /*
1116 * Note that the speed is stored in the c_cflag termios field, so we have
70bedfa1
KZ
1117 * set the speed always when the cflag se reseted.
1118 */
1119 cfsetispeed(tp, ispeed);
1120 cfsetospeed(tp, ospeed);
1121
ef264c83
KZ
1122 /* The default is to follow setting from kernel, but it's possible
1123 * to explicitly remove/add CLOCAL flag by -L[=<mode>]*/
1124 switch (op->clocal) {
1125 case CLOCAL_MODE_ALWAYS:
1126 tp->c_cflag |= CLOCAL; /* -L or -L=always */
1127 break;
1128 case CLOCAL_MODE_NEVER:
1129 tp->c_cflag &= ~CLOCAL; /* -L=never */
1130 break;
1131 case CLOCAL_MODE_AUTO: /* -L=auto */
1132 break;
1133 }
1134
30e8b186 1135#ifdef HAVE_STRUCT_TERMIOS_C_LINE
70bedfa1 1136 tp->c_line = 0;
1961482a 1137#endif
70bedfa1
KZ
1138 tp->c_cc[VMIN] = 1;
1139 tp->c_cc[VTIME] = 0;
7eda085c 1140
1683f6bf
DWF
1141 /* Check for terminal size and if not found set default */
1142 if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == 0) {
1143 int set = 0;
1144 if (ws.ws_row == 0) {
1145 ws.ws_row = 24;
1146 set++;
1147 }
1148 if (ws.ws_col == 0) {
1149 ws.ws_col = 80;
1150 set++;
1151 }
efcf26f4
KZ
1152 if (ioctl(STDIN_FILENO, TIOCSWINSZ, &ws))
1153 debug("TIOCSWINSZ ioctl failed\n");
1683f6bf
DWF
1154 }
1155
53d55042 1156 /* Optionally enable hardware flow control. */
7eda085c 1157#ifdef CRTSCTS
70bedfa1
KZ
1158 if (op->flags & F_RTSCTS)
1159 tp->c_cflag |= CRTSCTS;
7eda085c
KZ
1160#endif
1161
53d55042 1162 tcsetattr(STDIN_FILENO, TCSANOW, tp);
fd6b7a7f 1163
53d55042
SK
1164 /* Go to blocking input even in local mode. */
1165 fcntl(STDIN_FILENO, F_SETFL,
1166 fcntl(STDIN_FILENO, F_GETFL, 0) & ~O_NONBLOCK);
fd6b7a7f 1167
70bedfa1 1168 debug("term_io 2\n");
6dbe3af9
KZ
1169}
1170
0da025da
WF
1171/* Reset virtual console on stdin to its defaults */
1172static void reset_vc(const struct options *op, struct termios *tp)
1173{
879a7ab4
KZ
1174 int fl = 0;
1175
1176 fl |= (op->flags & F_KEEPCFLAGS) == 0 ? 0 : UL_TTY_KEEPCFLAGS;
1177 fl |= (op->flags & F_UTF8) == 0 ? 0 : UL_TTY_UTF8;
1178
1179 reset_virtual_console(tp, fl);
0da025da 1180
0da025da 1181 if (tcsetattr(STDIN_FILENO, TCSADRAIN, tp))
8c219bf4 1182 log_warn(_("setting terminal attributes failed: %m"));
0da025da
WF
1183}
1184
53d55042 1185/* Extract baud rate from modem status message. */
bde20e1b 1186static void auto_baud(struct termios *tp)
6dbe3af9 1187{
bde20e1b 1188 speed_t speed;
53d55042 1189 int vmin;
70bedfa1 1190 unsigned iflag;
53d55042
SK
1191 char buf[BUFSIZ];
1192 char *bp;
1193 int nread;
70bedfa1
KZ
1194
1195 /*
1196 * This works only if the modem produces its status code AFTER raising
1197 * the DCD line, and if the computer is fast enough to set the proper
1198 * baud rate before the message has gone by. We expect a message of the
1199 * following format:
1200 *
1201 * <junk><number><junk>
1202 *
1203 * The number is interpreted as the baud rate of the incoming call. If the
1204 * modem does not tell us the baud rate within one second, we will keep
1205 * using the current baud rate. It is advisable to enable BREAK
1206 * processing (comma-separated list of baud rates) if the processing of
1207 * modem status messages is enabled.
1208 */
1209
1210 /*
1211 * Use 7-bit characters, don't block if input queue is empty. Errors will
53d55042 1212 * be dealt with later on.
70bedfa1 1213 */
70bedfa1 1214 iflag = tp->c_iflag;
53d55042
SK
1215 /* Enable 8th-bit stripping. */
1216 tp->c_iflag |= ISTRIP;
70bedfa1 1217 vmin = tp->c_cc[VMIN];
53d55042
SK
1218 /* Do not block when queue is empty. */
1219 tp->c_cc[VMIN] = 0;
70bedfa1
KZ
1220 tcsetattr(STDIN_FILENO, TCSANOW, tp);
1221
1222 /*
1223 * Wait for a while, then read everything the modem has said so far and
1224 * try to extract the speed of the dial-in call.
1225 */
53d55042 1226 sleep(1);
70bedfa1
KZ
1227 if ((nread = read(STDIN_FILENO, buf, sizeof(buf) - 1)) > 0) {
1228 buf[nread] = '\0';
53d55042 1229 for (bp = buf; bp < buf + nread; bp++)
70bedfa1
KZ
1230 if (isascii(*bp) && isdigit(*bp)) {
1231 if ((speed = bcode(bp))) {
1232 cfsetispeed(tp, speed);
1233 cfsetospeed(tp, speed);
1234 }
1235 break;
1236 }
6dbe3af9 1237 }
6dbe3af9 1238
53d55042 1239 /* Restore terminal settings. Errors will be dealt with later on. */
70bedfa1
KZ
1240 tp->c_iflag = iflag;
1241 tp->c_cc[VMIN] = vmin;
53d55042 1242 tcsetattr(STDIN_FILENO, TCSANOW, tp);
6dbe3af9
KZ
1243}
1244
2448f336
KZ
1245static char *xgethostname(void)
1246{
1247 char *name;
1248 size_t sz = get_hostname_max() + 1;
1249
1250 name = malloc(sizeof(char) * sz);
1251 if (!name)
1252 log_err(_("failed to allocate memory: %m"));
1253
8362545b
KZ
1254 if (gethostname(name, sz) != 0) {
1255 free(name);
2448f336 1256 return NULL;
8362545b 1257 }
2448f336
KZ
1258 name[sz - 1] = '\0';
1259 return name;
1260}
1261
1262static char *xgetdomainname(void)
1263{
1264#ifdef HAVE_GETDOMAINNAME
1265 char *name;
1266 size_t sz = get_hostname_max() + 1;
1267
1268 name = malloc(sizeof(char) * sz);
1269 if (!name)
1270 log_err(_("failed to allocate memory: %m"));
1271
8362545b
KZ
1272 if (getdomainname(name, sz) != 0) {
1273 free(name);
2448f336 1274 return NULL;
8362545b 1275 }
2448f336
KZ
1276 name[sz - 1] = '\0';
1277 return name;
1278#endif
1279 return NULL;
1280}
1281
53d55042 1282/* Show login prompt, optionally preceded by /etc/issue contents. */
bde20e1b 1283static void do_prompt(struct options *op, struct termios *tp)
6dbe3af9
KZ
1284{
1285#ifdef ISSUE
53d55042 1286 FILE *fd;
53d55042 1287#endif /* ISSUE */
6dbe3af9 1288
e85281a8
DWF
1289 if ((op->flags & F_NONL) == 0) {
1290 /* Issue not in use, start with a new line. */
1291 write_all(STDOUT_FILENO, "\r\n", 2);
1292 }
3aa6b68f 1293
53d55042 1294#ifdef ISSUE
70bedfa1 1295 if ((op->flags & F_ISSUE) && (fd = fopen(op->issue, "r"))) {
1683f6bf 1296 int c, oflag = tp->c_oflag; /* Save current setting. */
3aa6b68f 1297
1683f6bf
DWF
1298 if ((op->flags & F_VCONSOLE) == 0) {
1299 /* Map new line in output to carriage return & new line. */
1300 tp->c_oflag |= (ONLCR | OPOST);
1301 tcsetattr(STDIN_FILENO, TCSADRAIN, tp);
1302 }
70bedfa1
KZ
1303
1304 while ((c = getc(fd)) != EOF) {
729def03 1305 if (c == '\\')
2b945eda 1306 output_special_char(getc(fd), op, tp, fd);
729def03 1307 else
763d7a87 1308 putchar(c);
70bedfa1
KZ
1309 }
1310 fflush(stdout);
6dbe3af9 1311
1683f6bf
DWF
1312 if ((op->flags & F_VCONSOLE) == 0) {
1313 /* Restore settings. */
1314 tp->c_oflag = oflag;
1315 /* Wait till output is gone. */
1316 tcsetattr(STDIN_FILENO, TCSADRAIN, tp);
1317 }
53d55042 1318 fclose(fd);
6dbe3af9 1319 }
eb8e1f9f
WF
1320#endif /* ISSUE */
1321 if (op->flags & F_LOGINPAUSE) {
8c219bf4 1322 puts(_("[press ENTER to login]"));
eb8e1f9f
WF
1323 getc(stdin);
1324 }
1325#ifdef KDGKBLED
36601b23
KZ
1326 if (!(op->flags & F_NOHINTS) && !op->autolog &&
1327 (op->flags & F_VCONSOLE)) {
a694957a
KZ
1328 int kb = 0;
1329
eb8e1f9f 1330 if (ioctl(STDIN_FILENO, KDGKBLED, &kb) == 0) {
a694957a
KZ
1331 char hint[256] = { '\0' };
1332 int nl = 0;
a694957a 1333
01c5b787 1334 if (access(_PATH_NUMLOCK_ON, F_OK) == 0)
a694957a
KZ
1335 nl = 1;
1336
1337 if (nl && (kb & 0x02) == 0)
1338 append(hint, sizeof(hint), NULL, _("Num Lock off"));
1339
1340 else if (nl == 0 && (kb & 2) && (kb & 0x20) == 0)
1341 append(hint, sizeof(hint), NULL, _("Num Lock on"));
1342
1343 if ((kb & 0x04) && (kb & 0x40) == 0)
1344 append(hint, sizeof(hint), ", ", _("Caps Lock on"));
1345
1346 if ((kb & 0x01) && (kb & 0x10) == 0)
1347 append(hint, sizeof(hint), ", ", _("Scroll Lock on"));
1348
1349 if (*hint)
1350 printf(_("Hint: %s\n\n"), hint);
eb8e1f9f
WF
1351 }
1352 }
1353#endif /* KDGKBLED */
e85281a8 1354 if ((op->flags & F_NOHOSTNAME) == 0) {
2448f336
KZ
1355 char *hn = xgethostname();
1356
1357 if (hn) {
e85281a8 1358 char *dot = strchr(hn, '.');
74b3df85
SK
1359 char *cn = hn;
1360 struct addrinfo *res = NULL;
e85281a8 1361
e85281a8
DWF
1362 if ((op->flags & F_LONGHNAME) == 0) {
1363 if (dot)
1364 *dot = '\0';
74b3df85
SK
1365
1366 } else if (dot == NULL) {
1367 struct addrinfo hints;
1368
1369 memset(&hints, 0, sizeof(hints));
1370 hints.ai_flags = AI_CANONNAME;
1371
1372 if (!getaddrinfo(hn, NULL, &hints, &res)
1373 && res && res->ai_canonname)
1374 cn = res->ai_canonname;
1375 }
1376
1377 write_all(STDOUT_FILENO, cn, strlen(cn));
e85281a8 1378 write_all(STDOUT_FILENO, " ", 1);
74b3df85
SK
1379
1380 if (res)
1381 freeaddrinfo(res);
2448f336 1382 free(hn);
e85281a8 1383 }
70bedfa1 1384 }
933956cb 1385 if (!op->autolog) {
eb8e1f9f
WF
1386 /* Always show login prompt. */
1387 write_all(STDOUT_FILENO, LOGIN, sizeof(LOGIN) - 1);
1388 }
6dbe3af9
KZ
1389}
1390
53d55042 1391/* Select next baud rate. */
bde20e1b 1392static void next_speed(struct options *op, struct termios *tp)
6dbe3af9 1393{
70bedfa1
KZ
1394 static int baud_index = -1;
1395
1396 if (baud_index == -1)
1397 /*
53d55042 1398 * If the F_KEEPSPEED flags is set then the FIRST_SPEED is not
70bedfa1
KZ
1399 * tested yet (see termio_init()).
1400 */
53d55042
SK
1401 baud_index =
1402 (op->flags & F_KEEPSPEED) ? FIRST_SPEED : 1 % op->numspeed;
70bedfa1
KZ
1403 else
1404 baud_index = (baud_index + 1) % op->numspeed;
1405
1406 cfsetispeed(tp, op->speeds[baud_index]);
1407 cfsetospeed(tp, op->speeds[baud_index]);
53d55042 1408 tcsetattr(STDIN_FILENO, TCSANOW, tp);
6dbe3af9
KZ
1409}
1410
53d55042 1411/* Get user name, establish parity, speed, erase, kill & eol. */
bde20e1b 1412static char *get_logname(struct options *op, struct termios *tp, struct chardata *cp)
6dbe3af9 1413{
70bedfa1 1414 static char logname[BUFSIZ];
53d55042
SK
1415 char *bp;
1416 char c; /* input character, full eight bits */
1417 char ascval; /* low 7 bits of input character */
1683f6bf 1418 int eightbit;
53d55042
SK
1419 static char *erase[] = { /* backspace-space-backspace */
1420 "\010\040\010", /* space parity */
1421 "\010\040\010", /* odd parity */
1422 "\210\240\210", /* even parity */
1423 "\210\240\210", /* no parity */
70bedfa1
KZ
1424 };
1425
1426 /* Initialize kill, erase, parity etc. (also after switching speeds). */
f5664477 1427 INIT_CHARDATA(cp);
70bedfa1 1428
53d55042
SK
1429 /*
1430 * Flush pending input (especially important after parsing or switching
1431 * the baud rate).
1432 */
1683f6bf
DWF
1433 if ((op->flags & F_VCONSOLE) == 0)
1434 sleep(1);
53d55042 1435 tcflush(STDIN_FILENO, TCIFLUSH);
70bedfa1 1436
1683f6bf
DWF
1437 eightbit = (op->flags & F_EIGHTBITS);
1438 bp = logname;
1439 *bp = '\0';
1440
1441 while (*logname == '\0') {
1442
1443 /* Write issue file and prompt */
70bedfa1
KZ
1444 do_prompt(op, tp);
1445
1683f6bf
DWF
1446 cp->eol = '\0';
1447
1448 /* Read name, watch for break and end-of-line. */
1449 while (cp->eol == '\0') {
1450
cb872ac9
KZ
1451 char key;
1452
763d7a87 1453 if (read(STDIN_FILENO, &c, 1) < 1) {
1683f6bf 1454
714cff30
KZ
1455 /* The terminal could be open with O_NONBLOCK when
1456 * -L (force CLOCAL) is specified... */
1683f6bf 1457 if (errno == EINTR || errno == EAGAIN) {
714cff30 1458 usleep(250000);
1683f6bf
DWF
1459 continue;
1460 }
1461 switch (errno) {
1462 case 0:
1463 case EIO:
1464 case ESRCH:
1465 case EINVAL:
1466 case ENOENT:
1467 break;
1468 default:
1469 log_err(_("%s: read: %m"), op->tty);
1470 }
70bedfa1 1471 }
1683f6bf 1472
70bedfa1 1473 /* Do parity bit handling. */
1683f6bf 1474 if (eightbit)
70bedfa1 1475 ascval = c;
1683f6bf
DWF
1476 else if (c != (ascval = (c & 0177))) {
1477 uint32_t bits; /* # of "1" bits per character */
1478 uint32_t mask; /* mask with 1 bit up */
1479 for (bits = 1, mask = 1; mask & 0177; mask <<= 1) {
70bedfa1 1480 if (mask & ascval)
53d55042 1481 bits++;
1683f6bf 1482 }
70bedfa1
KZ
1483 cp->parity |= ((bits & 1) ? 1 : 2);
1484 }
1683f6bf 1485
cb872ac9
KZ
1486 if (op->killchars && strchr(op->killchars, ascval))
1487 key = CTL('U');
1488 else if (op->erasechars && strchr(op->erasechars, ascval))
1489 key = DEL;
1490 else
1491 key = ascval;
1492
70bedfa1 1493 /* Do erase, kill and end-of-line processing. */
cb872ac9 1494 switch (key) {
1683f6bf
DWF
1495 case 0:
1496 *bp = 0;
1497 if (op->numspeed > 1)
4a9b7543 1498 return NULL;
1683f6bf 1499 break;
70bedfa1
KZ
1500 case CR:
1501 case NL:
1683f6bf
DWF
1502 *bp = 0; /* terminate logname */
1503 cp->eol = ascval; /* set end-of-line char */
70bedfa1
KZ
1504 break;
1505 case BS:
1506 case DEL:
1683f6bf 1507 cp->erase = ascval; /* set erase character */
70bedfa1 1508 if (bp > logname) {
b9261127 1509 if ((tp->c_lflag & ECHO) == 0)
1683f6bf 1510 write_all(1, erase[cp->parity], 3);
70bedfa1
KZ
1511 bp--;
1512 }
1513 break;
1514 case CTL('U'):
1683f6bf 1515 cp->kill = ascval; /* set kill character */
70bedfa1 1516 while (bp > logname) {
b9261127 1517 if ((tp->c_lflag & ECHO) == 0)
1683f6bf 1518 write_all(1, erase[cp->parity], 3);
70bedfa1
KZ
1519 bp--;
1520 }
1521 break;
1522 case CTL('D'):
1523 exit(EXIT_SUCCESS);
1524 default:
1683f6bf
DWF
1525 if (!isascii(ascval) || !isprint(ascval))
1526 break;
1527 if ((size_t)(bp - logname) >= sizeof(logname) - 1)
bde20e1b 1528 log_err(_("%s: input overrun"), op->tty);
b9261127 1529 if ((tp->c_lflag & ECHO) == 0)
1683f6bf
DWF
1530 write_all(1, &c, 1); /* echo the character */
1531 *bp++ = ascval; /* and store it */
70bedfa1
KZ
1532 break;
1533 }
6dbe3af9 1534 }
6dbe3af9 1535 }
1683f6bf
DWF
1536#ifdef HAVE_WIDECHAR
1537 if ((op->flags & (F_EIGHTBITS|F_UTF8)) == (F_EIGHTBITS|F_UTF8)) {
1538 /* Check out UTF-8 multibyte characters */
1539 ssize_t len;
1540 wchar_t *wcs, *wcp;
1541
1542 len = mbstowcs((wchar_t *)0, logname, 0);
1543 if (len < 0)
8c219bf4 1544 log_err(_("%s: invalid character conversion for login name"), op->tty);
1683f6bf 1545
ca8e91a4
KZ
1546 wcs = (wchar_t *) malloc((len + 1) * sizeof(wchar_t));
1547 if (!wcs)
1548 log_err(_("failed to allocate memory: %m"));
1683f6bf
DWF
1549
1550 len = mbstowcs(wcs, logname, len + 1);
1551 if (len < 0)
8c219bf4 1552 log_err(_("%s: invalid character conversion for login name"), op->tty);
1683f6bf
DWF
1553
1554 wcp = wcs;
1555 while (*wcp) {
1556 const wint_t wc = *wcp++;
1557 if (!iswprint(wc))
8c219bf4 1558 log_err(_("%s: invalid character 0x%x in login name"), op->tty, wc);
1683f6bf
DWF
1559 }
1560 free(wcs);
1561 } else
1562#endif
1563 if ((op->flags & F_LCUC) && (cp->capslock = caps_lock(logname))) {
1564
1565 /* Handle names with upper case and no lower case. */
70bedfa1
KZ
1566 for (bp = logname; *bp; bp++)
1567 if (isupper(*bp))
1683f6bf
DWF
1568 *bp = tolower(*bp); /* map name to lower case */
1569 }
1570
70bedfa1 1571 return logname;
6dbe3af9
KZ
1572}
1573
53d55042 1574/* Set the final tty mode bits. */
bde20e1b 1575static void termio_final(struct options *op, struct termios *tp, struct chardata *cp)
6dbe3af9 1576{
70bedfa1 1577 /* General terminal-independent stuff. */
53d55042
SK
1578
1579 /* 2-way flow control */
1580 tp->c_iflag |= IXON | IXOFF;
1581 tp->c_lflag |= ICANON | ISIG | ECHO | ECHOE | ECHOK | ECHOKE;
1582 /* no longer| ECHOCTL | ECHOPRT */
70bedfa1
KZ
1583 tp->c_oflag |= OPOST;
1584 /* tp->c_cflag = 0; */
53d55042
SK
1585 tp->c_cc[VINTR] = DEF_INTR;
1586 tp->c_cc[VQUIT] = DEF_QUIT;
1587 tp->c_cc[VEOF] = DEF_EOF;
70bedfa1 1588 tp->c_cc[VEOL] = DEF_EOL;
fd6b7a7f 1589#ifdef __linux__
53d55042 1590 tp->c_cc[VSWTC] = DEF_SWITCH;
1961482a 1591#elif defined(VSWTCH)
53d55042
SK
1592 tp->c_cc[VSWTCH] = DEF_SWITCH;
1593#endif /* __linux__ */
6dbe3af9 1594
70bedfa1
KZ
1595 /* Account for special characters seen in input. */
1596 if (cp->eol == CR) {
53d55042
SK
1597 tp->c_iflag |= ICRNL;
1598 tp->c_oflag |= ONLCR;
70bedfa1 1599 }
53d55042
SK
1600 tp->c_cc[VERASE] = cp->erase;
1601 tp->c_cc[VKILL] = cp->kill;
70bedfa1
KZ
1602
1603 /* Account for the presence or absence of parity bits in input. */
1604 switch (cp->parity) {
53d55042
SK
1605 case 0:
1606 /* space (always 0) parity */
1607 break;
1608 case 1:
1609 /* odd parity */
1610 tp->c_cflag |= PARODD;
1611 /* do not break */
1612 case 2:
1613 /* even parity */
1614 tp->c_cflag |= PARENB;
1615 tp->c_iflag |= INPCK | ISTRIP;
1616 /* do not break */
1617 case (1 | 2):
1618 /* no parity bit */
1619 tp->c_cflag &= ~CSIZE;
1620 tp->c_cflag |= CS7;
1621 break;
70bedfa1
KZ
1622 }
1623 /* Account for upper case without lower case. */
1624 if (cp->capslock) {
ee519041 1625#ifdef IUCLC
70bedfa1 1626 tp->c_iflag |= IUCLC;
ee519041 1627#endif
b75c8134 1628#ifdef XCASE
70bedfa1 1629 tp->c_lflag |= XCASE;
1961482a 1630#endif
ee519041 1631#ifdef OLCUC
70bedfa1 1632 tp->c_oflag |= OLCUC;
ee519041 1633#endif
70bedfa1 1634 }
53d55042 1635 /* Optionally enable hardware flow control. */
6dbe3af9 1636#ifdef CRTSCTS
70bedfa1
KZ
1637 if (op->flags & F_RTSCTS)
1638 tp->c_cflag |= CRTSCTS;
6dbe3af9
KZ
1639#endif
1640
53d55042 1641 /* Finally, make the new settings effective. */
70bedfa1 1642 if (tcsetattr(STDIN_FILENO, TCSANOW, tp) < 0)
8c219bf4 1643 log_err(_("%s: failed to set terminal attributes: %m"), op->tty);
6dbe3af9
KZ
1644}
1645
53d55042
SK
1646/*
1647 * String contains upper case without lower case.
1648 * http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=52940
1649 * http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=156242
1650 */
bde20e1b 1651static int caps_lock(char *s)
6dbe3af9 1652{
53d55042 1653 int capslock;
70bedfa1
KZ
1654
1655 for (capslock = 0; *s; s++) {
1656 if (islower(*s))
1657 return EXIT_SUCCESS;
1658 if (capslock == 0)
1659 capslock = isupper(*s);
1660 }
1661 return capslock;
6dbe3af9
KZ
1662}
1663
53d55042 1664/* Convert speed string to speed code; return 0 on failure. */
bde20e1b 1665static speed_t bcode(char *s)
6dbe3af9 1666{
1a204cd2 1667 const struct Speedtab *sp;
53d55042 1668 long speed = atol(s);
6dbe3af9 1669
70bedfa1
KZ
1670 for (sp = speedtab; sp->speed; sp++)
1671 if (sp->speed == speed)
1672 return sp->code;
1673 return 0;
6dbe3af9
KZ
1674}
1675
7d368556 1676static void __attribute__ ((__noreturn__)) usage(FILE *out)
6dbe3af9 1677{
7d368556 1678 fputs(USAGE_HEADER, out);
4cfda3dc 1679 fprintf(out, _(" %1$s [options] line [baud_rate,...] [termtype]\n"
7d368556
SK
1680 " %1$s [options] baud_rate,... line [termtype]\n"), program_invocation_short_name);
1681 fputs(USAGE_OPTIONS, out);
1682 fputs(_(" -8, --8bits assume 8-bit tty\n"), out);
1683 fputs(_(" -a, --autologin <user> login the specified user automatically\n"), out);
1684 fputs(_(" -c, --noreset do not reset control mode\n"), out);
1505a0b2 1685 fputs(_(" -E, --remote use -r <hostname> for login(1)\n"), out);
7d368556
SK
1686 fputs(_(" -f, --issue-file <file> display issue file\n"), out);
1687 fputs(_(" -h, --flow-control enable hardware flow control\n"), out);
1688 fputs(_(" -H, --host <hostname> specify login host\n"), out);
1689 fputs(_(" -i, --noissue do not display issue file\n"), out);
1690 fputs(_(" -I, --init-string <string> set init string\n"), out);
1691 fputs(_(" -l, --login-program <file> specify login program\n"), out);
ef264c83 1692 fputs(_(" -L, --local-line[=<mode>] cotrol local line flag\n"), out);
7d368556
SK
1693 fputs(_(" -m, --extract-baud extract baud rate during connect\n"), out);
1694 fputs(_(" -n, --skip-login do not prompt for login\n"), out);
1695 fputs(_(" -o, --login-options <opts> options that are passed to login\n"), out);
a056cfe2 1696 fputs(_(" -p, --login-pause wait for any key before the login\n"), out);
4b856ee4 1697 fputs(_(" -r, --chroot <dir> change root to the directory\n"), out);
7d368556
SK
1698 fputs(_(" -R, --hangup do virtually hangup on the tty\n"), out);
1699 fputs(_(" -s, --keep-baud try to keep baud rate after break\n"), out);
1700 fputs(_(" -t, --timeout <number> login process timeout\n"), out);
1701 fputs(_(" -U, --detect-case detect uppercase terminal\n"), out);
1702 fputs(_(" -w, --wait-cr wait carriage-return\n"), out);
1703 fputs(_(" --noclear do not clear the screen before prompt\n"), out);
1704 fputs(_(" --nohints do not print hints\n"), out);
1705 fputs(_(" --nonewline do not print a newline before issue\n"), out);
a056cfe2 1706 fputs(_(" --nohostname no hostname at all will be shown\n"), out);
7d368556
SK
1707 fputs(_(" --long-hostname show full qualified hostname\n"), out);
1708 fputs(_(" --erase-chars <string> additional backspace chars\n"), out);
1709 fputs(_(" --kill-chars <string> additional kill chars\n"), out);
86ad1432
SK
1710 fputs(_(" --help display this help and exit\n"), out);
1711 fputs(_(" --version output version information and exit\n"), out);
7d368556 1712 fprintf(out, USAGE_MAN_TAIL("agetty(8)"));
70bedfa1
KZ
1713
1714 exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
6dbe3af9
KZ
1715}
1716
bde20e1b
WF
1717/*
1718 * Helper function reports errors to console or syslog.
1719 * Will be used by log_err() and log_warn() therefore
1720 * it takes a format as well as va_list.
1721 */
6dbe3af9
KZ
1722#define str2cpy(b,s1,s2) strcat(strcpy(b,s1),s2)
1723
bde20e1b 1724static void dolog(int priority, const char *fmt, va_list ap)
53d55042 1725{
6dbe3af9 1726#ifndef USE_SYSLOG
53d55042 1727 int fd;
6dbe3af9 1728#endif
53d55042
SK
1729 char buf[BUFSIZ];
1730 char *bp;
6dbe3af9 1731
70bedfa1
KZ
1732 /*
1733 * If the diagnostic is reported via syslog(3), the process name is
1734 * automatically prepended to the message. If we write directly to
1735 * /dev/console, we must prepend the process name ourselves.
1736 */
6dbe3af9 1737#ifdef USE_SYSLOG
70bedfa1
KZ
1738 buf[0] = '\0';
1739 bp = buf;
6dbe3af9 1740#else
53d55042 1741 str2cpy(buf, program_invocation_short_name, ": ");
70bedfa1 1742 bp = buf + strlen(buf);
53d55042 1743#endif /* USE_SYSLOG */
763d7a87 1744 vsnprintf(bp, sizeof(buf)-strlen(buf), fmt, ap);
6dbe3af9 1745
70bedfa1
KZ
1746 /*
1747 * Write the diagnostic directly to /dev/console if we do not use the
1748 * syslog(3) facility.
1749 */
6dbe3af9 1750#ifdef USE_SYSLOG
53d55042 1751 openlog(program_invocation_short_name, LOG_PID, LOG_AUTHPRIV);
bde20e1b 1752 syslog(priority, "%s", buf);
70bedfa1 1753 closelog();
6dbe3af9 1754#else
70bedfa1 1755 /* Terminate with CR-LF since the console mode is unknown. */
53d55042 1756 strcat(bp, "\r\n");
70bedfa1 1757 if ((fd = open("/dev/console", 1)) >= 0) {
729def03 1758 write_all(fd, buf, strlen(buf));
53d55042 1759 close(fd);
70bedfa1 1760 }
53d55042 1761#endif /* USE_SYSLOG */
bde20e1b
WF
1762}
1763
1764static void log_err(const char *fmt, ...)
1765{
1766 va_list ap;
1767
1768 va_start(ap, fmt);
1769 dolog(LOG_ERR, fmt, ap);
1770 va_end(ap);
1771
53d55042
SK
1772 /* Be kind to init(8). */
1773 sleep(10);
70bedfa1 1774 exit(EXIT_FAILURE);
6dbe3af9 1775}
bde20e1b
WF
1776
1777static void log_warn(const char *fmt, ...)
1778{
1779 va_list ap;
1780
1781 va_start(ap, fmt);
1782 dolog(LOG_WARNING, fmt, ap);
1783 va_end(ap);
1784}
729def03 1785
2b945eda
KZ
1786static void output_iface_ip(struct ifaddrs *addrs, const char *iface, sa_family_t family)
1787{
1788 if (!iface)
1789 return;
1790
1791 if (addrs->ifa_name
1792 && strcmp(addrs->ifa_name, iface) == 0
1793 && addrs->ifa_addr
1794 && addrs->ifa_addr->sa_family == family) {
1795
1796 void *addr = NULL;
1797 char buff[INET6_ADDRSTRLEN + 1];
1798
1799 switch (addrs->ifa_addr->sa_family) {
1800 case AF_INET:
1801 addr = &((struct sockaddr_in *) addrs->ifa_addr)->sin_addr;
1802 break;
1803 case AF_INET6:
1804 addr = &((struct sockaddr_in6 *) addrs->ifa_addr)->sin6_addr;
1805 break;
1806 }
1807 if (addr) {
1808 inet_ntop(addrs->ifa_addr->sa_family, addr, buff, sizeof(buff));
1809 printf("%s", buff);
1810 }
1811
1812 } else if (addrs->ifa_next)
1813 output_iface_ip(addrs->ifa_next, iface, family);
1814}
1815
1816static void output_ip(sa_family_t family)
1817{
2448f336 1818 char *host;
2b945eda
KZ
1819 struct addrinfo hints, *info = NULL;
1820
1821 memset(&hints, 0, sizeof(hints));
1822 hints.ai_family = family;
1823 if (family == AF_INET6)
1824 hints.ai_flags = AI_V4MAPPED;
1825
2448f336
KZ
1826 host = xgethostname();
1827 if (host && getaddrinfo(host, NULL, &hints, &info) == 0 && info) {
2b945eda
KZ
1828
1829 void *addr = NULL;
2b945eda
KZ
1830
1831 switch (info->ai_family) {
1832 case AF_INET:
1833 addr = &((struct sockaddr_in *) info->ai_addr)->sin_addr;
1834 break;
1835 case AF_INET6:
1836 addr = &((struct sockaddr_in6 *) info->ai_addr)->sin6_addr;
1837 break;
1838 }
915c1d99
KZ
1839 if (addr) {
1840 char buff[INET6_ADDRSTRLEN + 1];
1841
1842 inet_ntop(info->ai_family, (void *) addr, buff, sizeof(buff));
1843 printf("%s", buff);
1844 }
2b945eda
KZ
1845
1846 freeaddrinfo(info);
1847 }
2448f336 1848 free(host);
2b945eda
KZ
1849}
1850
1851/*
1852 * parses \x{argument}, if not argument specified then returns NULL, the @fd
1853 * has to point to one char after the sequence (it means '{').
1854 */
1855static char *get_escape_argument(FILE *fd, char *buf, size_t bufsz)
1856{
1857 size_t i = 0;
1858 int c = fgetc(fd);
1859
1860 if (c == EOF || (unsigned char) c != '{') {
1861 ungetc(c, fd);
1862 return NULL;
1863 }
1864
1865 do {
1866 c = fgetc(fd);
1867 if (c == EOF)
1868 return NULL;
1869 if ((unsigned char) c != '}' && i < bufsz - 1)
1870 buf[i++] = (unsigned char) c;
1871
1872 } while ((unsigned char) c != '}');
1873
1874 buf[i] = '\0';
1875 return buf;
1876}
1877
729def03 1878static void output_special_char(unsigned char c, struct options *op,
2b945eda 1879 struct termios *tp, FILE *fp)
729def03
WF
1880{
1881 struct utsname uts;
763d7a87
KZ
1882
1883 uname(&uts);
3aa6b68f 1884
729def03
WF
1885 switch (c) {
1886 case 's':
763d7a87 1887 printf("%s", uts.sysname);
729def03
WF
1888 break;
1889 case 'n':
763d7a87 1890 printf("%s", uts.nodename);
729def03
WF
1891 break;
1892 case 'r':
763d7a87 1893 printf("%s", uts.release);
729def03
WF
1894 break;
1895 case 'v':
763d7a87 1896 printf("%s", uts.version);
729def03
WF
1897 break;
1898 case 'm':
763d7a87 1899 printf("%s", uts.machine);
729def03
WF
1900 break;
1901 case 'o':
1902 {
2448f336
KZ
1903 char *dom = xgetdomainname();
1904
1905 fputs(dom ? dom : "unknown_domain", stdout);
1906 free(dom);
729def03
WF
1907 break;
1908 }
1909 case 'O':
1910 {
2448f336
KZ
1911 char *dom = NULL;
1912 char *host = xgethostname();
729def03
WF
1913 struct addrinfo hints, *info = NULL;
1914
1915 memset(&hints, 0, sizeof(hints));
1916 hints.ai_flags = AI_CANONNAME;
1917
2448f336 1918 if (host && getaddrinfo(host, NULL, &hints, &info) == 0 && info) {
729def03 1919 char *canon;
2448f336 1920
729def03
WF
1921 if (info->ai_canonname &&
1922 (canon = strchr(info->ai_canonname, '.')))
1923 dom = canon + 1;
729def03 1924 }
2448f336
KZ
1925 fputs(dom ? dom : "unknown_domain", stdout);
1926 if (info)
1927 freeaddrinfo(info);
1928 free(host);
729def03
WF
1929 break;
1930 }
1931 case 'd':
1932 case 't':
1933 {
1934 time_t now;
1935 struct tm *tm;
1936
763d7a87 1937 time(&now);
729def03
WF
1938 tm = localtime(&now);
1939
d9201203
KZ
1940 if (!tm)
1941 break;
1942
729def03 1943 if (c == 'd') /* ISO 8601 */
763d7a87 1944 printf("%s %s %d %d",
729def03
WF
1945 nl_langinfo(ABDAY_1 + tm->tm_wday),
1946 nl_langinfo(ABMON_1 + tm->tm_mon),
1947 tm->tm_mday,
1948 tm->tm_year < 70 ? tm->tm_year + 2000 :
1949 tm->tm_year + 1900);
1950 else
763d7a87 1951 printf("%02d:%02d:%02d",
729def03
WF
1952 tm->tm_hour, tm->tm_min, tm->tm_sec);
1953 break;
1954 }
1955 case 'l':
763d7a87 1956 printf ("%s", op->tty);
729def03
WF
1957 break;
1958 case 'b':
1959 {
1960 const speed_t speed = cfgetispeed(tp);
1961 int i;
1962
1963 for (i = 0; speedtab[i].speed; i++) {
1964 if (speedtab[i].code == speed) {
1965 printf("%ld", speedtab[i].speed);
1966 break;
1967 }
1968 }
1969 break;
1970 }
1971 case 'u':
1972 case 'U':
1973 {
1974 int users = 0;
1975 struct utmp *ut;
1976 setutent();
1977 while ((ut = getutent()))
1978 if (ut->ut_type == USER_PROCESS)
1979 users++;
1980 endutent();
729def03 1981 if (c == 'U')
9e531400
BS
1982 printf(P_("%d user", "%d users", users), users);
1983 else
1984 printf ("%d ", users);
729def03
WF
1985 break;
1986 }
2b945eda
KZ
1987 case '4':
1988 case '6':
1989 {
1990 sa_family_t family = c == '4' ? AF_INET : AF_INET6;
1991 char iface[128];
1992
1993 if (get_escape_argument(fp, iface, sizeof(iface))) { /* interface IP */
1994 struct ifaddrs *addrs;
1995 int status = getifaddrs(&addrs);
1996 if (status != 0)
1997 break;
1998 output_iface_ip(addrs, iface, family);
1999 freeifaddrs(addrs);
2000 } else /* host IP */
2001 output_ip(family);
2002 break;
2003 }
729def03 2004 default:
763d7a87 2005 putchar(c);
729def03
WF
2006 break;
2007 }
2008}
2009
2010static void init_special_char(char* arg, struct options *op)
2011{
2012 char ch, *p, *q;
2013 int i;
2014
ca8e91a4
KZ
2015 op->initstring = malloc(strlen(arg) + 1);
2016 if (!op->initstring)
2017 log_err(_("failed to allocate memory: %m"));
729def03
WF
2018
2019 /*
2020 * Copy optarg into op->initstring decoding \ddd octal
2021 * codes into chars.
2022 */
2023 q = op->initstring;
2024 p = arg;
2025 while (*p) {
2026 /* The \\ is converted to \ */
2027 if (*p == '\\') {
2028 p++;
2029 if (*p == '\\') {
2030 ch = '\\';
2031 p++;
2032 } else {
2033 /* Handle \000 - \177. */
2034 ch = 0;
2035 for (i = 1; i <= 3; i++) {
2036 if (*p >= '0' && *p <= '7') {
2037 ch <<= 3;
2038 ch += *p - '0';
2039 p++;
2040 } else {
2041 break;
2042 }
2043 }
2044 }
2045 *q++ = ch;
2046 } else
2047 *q++ = *p++;
2048 }
2049 *q = '\0';
2050}
eb8e1f9f 2051
a694957a
KZ
2052/*
2053 * Appends @str to @dest and if @dest is not empty then use use @sep as a
2054 * separator. The maximal final length of the @dest is @len.
2055 *
2056 * Returns the final @dest length or -1 in case of error.
2057 */
2058static ssize_t append(char *dest, size_t len, const char *sep, const char *src)
2059{
2060 size_t dsz = 0, ssz = 0, sz;
2061 char *p;
2062
2063 if (!dest || !len || !src)
2064 return -1;
2065
2066 if (*dest)
2067 dsz = strlen(dest);
2068 if (dsz && sep)
2069 ssz = strlen(sep);
2070 sz = strlen(src);
2071
2072 if (dsz + ssz + sz + 1 > len)
2073 return -1;
2074
2075 p = dest + dsz;
2076 if (ssz) {
2077 memcpy(p, sep, ssz);
2078 p += ssz;
2079 }
2080 memcpy(p, src, sz);
2081 *(p + sz) = '\0';
2082
2083 return dsz + ssz + sz;
2084}
9aeb66dc 2085
eb8e1f9f
WF
2086/*
2087 * Do not allow the user to pass an option as a user name
2088 * To be more safe: Use `--' to make sure the rest is
2089 * interpreted as non-options by the program, if it supports it.
2090 */
9aeb66dc 2091static void check_username(const char* nm)
eb8e1f9f
WF
2092{
2093 const char *p = nm;
2094 if (!nm)
2095 goto err;
763d7a87 2096 if (strlen(nm) > 42)
eb8e1f9f 2097 goto err;
763d7a87 2098 while (isspace(*p))
eb8e1f9f
WF
2099 p++;
2100 if (*p == '-')
2101 goto err;
2102 return;
2103err:
2104 errno = EPERM;
8c219bf4 2105 log_err(_("checkname failed: %m"));
eb8e1f9f
WF
2106}
2107