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