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