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