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