From ea06937ceb23a4e0a266f588b7b91b8ef991aedb Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Mar 12 2019 16:39:05 +0000 Subject: import util-linux-2.23.2-59.el7_6.1 --- diff --git a/SOURCES/0179-agetty-backport-RHEL-8-version.patch b/SOURCES/0179-agetty-backport-RHEL-8-version.patch new file mode 100644 index 0000000..4a2bfad --- /dev/null +++ b/SOURCES/0179-agetty-backport-RHEL-8-version.patch @@ -0,0 +1,1912 @@ +From ea152b4e9656945f17d18bf4e5ece91e3e57ebb1 Mon Sep 17 00:00:00 2001 +From: Karel Zak +Date: Tue, 29 Jan 2019 11:57:55 +0100 +Subject: [PATCH] agetty: backport RHEL-8 version + +The code is identical to RHEL-8.0, except: + + * disabled ISSUEDIR_SUPPORT + * disabled AGETTY_RELOAD + * removed \e and \e{name} feature + +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1664752 +Signed-off-by: Karel Zak +--- + include/pathnames.h | 5 +- + term-utils/agetty.c | 1141 +++++++++++++++++++++++++++++++------------ + 2 files changed, 839 insertions(+), 307 deletions(-) + +diff --git a/include/pathnames.h b/include/pathnames.h +index fa4bddbad..ec5eef918 100644 +--- a/include/pathnames.h ++++ b/include/pathnames.h +@@ -64,9 +64,10 @@ + + /* used in term-utils/agetty.c */ + #define _PATH_ISSUE "/etc/issue" +-#define _PATH_OS_RELEASE "/etc/os-release" ++#define _PATH_ISSUEDIR _PATH_ISSUE ".d" ++#define _PATH_OS_RELEASE_ETC "/etc/os-release" ++#define _PATH_OS_RELEASE_USR "/usr/lib/os-release" + #define _PATH_NUMLOCK_ON _PATH_LOCALSTATEDIR "/numlock-on" +- + #define _PATH_LOGINDEFS "/etc/login.defs" + + /* used in misc-utils/look.c */ +diff --git a/term-utils/agetty.c b/term-utils/agetty.c +index 948d77246..c3c7ab504 100644 +--- a/term-utils/agetty.c ++++ b/term-utils/agetty.c +@@ -10,6 +10,7 @@ + * + * This program is freely distributable. + */ ++ + #include + #include + #include +@@ -20,19 +21,21 @@ + #include + #include + #include ++#include + #include + #include + #include +-#include ++#include + #include + #include +-#include + #include + #include + #include + #include + #include + #include ++#include ++#include + + #include "strutils.h" + #include "all-io.h" +@@ -41,14 +44,37 @@ + #include "c.h" + #include "widechar.h" + #include "ttyutils.h" ++#include "env.h" ++ ++#ifdef USE_PLYMOUTH_SUPPORT ++# include "plymouth-ctrl.h" ++#endif ++ ++#ifdef HAVE_SYS_PARAM_H ++# include ++#endif ++ ++#if defined(__FreeBSD_kernel__) ++# include ++# ifdef HAVE_UTMP_H ++# include ++# endif ++# ifdef HAVE_LIBUTIL_H ++# include ++# endif ++#endif + + #ifdef __linux__ + # include +-# include + # define USE_SYSLOG + # ifndef DEFAULT_VCTERM + # define DEFAULT_VCTERM "linux" + # endif ++# if defined (__s390__) || defined (__s390x__) ++# define DEFAULT_TTYS0 "dumb" ++# define DEFAULT_TTY32 "ibm327x" ++# define DEFAULT_TTYS1 "vt220" ++# endif + # ifndef DEFAULT_STERM + # define DEFAULT_STERM "vt102" + # endif +@@ -69,6 +95,10 @@ + # endif + #endif + ++#ifdef __FreeBSD_kernel__ ++#define USE_SYSLOG ++#endif ++ + /* If USE_SYSLOG is undefined all diagnostics go to /dev/console. */ + #ifdef USE_SYSLOG + # include +@@ -86,9 +116,9 @@ + /* + * Things you may want to modify. + * +- * If ISSUE is not defined, agetty will never display the contents of the +- * /etc/issue file. You will not want to spit out large "issue" files at the +- * wrong baud rate. Relevant for System V only. ++ * If ISSUE_SUPPORT is not defined, agetty will never display the contents of ++ * the /etc/issue file. You will not want to spit out large "issue" files at ++ * the wrong baud rate. Relevant for System V only. + * + * You may disagree with the default line-editing etc. characters defined + * below. Note, however, that DEL cannot be used for interrupt generation +@@ -97,14 +127,26 @@ + + /* Displayed before the login prompt. */ + #ifdef SYSV_STYLE +-# define ISSUE _PATH_ISSUE +-# include ++# define ISSUE_SUPPORT + #endif + + /* Login prompt. */ + #define LOGIN "login: " + #define LOGIN_ARGV_MAX 16 /* Numbers of args for login */ + ++/* ++ * agetty --reload ++ */ ++#ifdef AGETTY_RELOAD ++# include ++# include ++# include ++# define AGETTY_RELOAD_FILENAME "/run/agetty.reload" /* trigger file */ ++# define AGETTY_RELOAD_FDNONE -2 /* uninitialized fd */ ++static int inotify_fd = AGETTY_RELOAD_FDNONE; ++static int netlink_fd = AGETTY_RELOAD_FDNONE; ++#endif ++ + /* + * When multiple baud rates are specified on the command line, the first one + * we will try is the first one specified. +@@ -116,7 +158,7 @@ + + struct options { + int flags; /* toggle switches, see below */ +- int timeout; /* time-out period */ ++ unsigned int timeout; /* time-out period */ + char *autolog; /* login the user automatically */ + char *chdir; /* Chdir before the login */ + char *chroot; /* Chroot before the login */ +@@ -126,11 +168,11 @@ struct options { + char *vcline; /* line of virtual console */ + char *term; /* terminal type */ + char *initstring; /* modem init string */ +- char *issue; /* alternative issue file */ ++ char *issue; /* alternative issue file or directory */ + char *erasechars; /* string with erase chars */ + char *killchars; /* string with kill chars */ + char *osrelease; /* /etc/os-release data */ +- int delay; /* Sleep seconds before prompt */ ++ unsigned int delay; /* Sleep seconds before prompt */ + int nice; /* Run login with this priority */ + int numspeed; /* number of baud rates to try */ + int clocal; /* CLOCAL_MODE_* */ +@@ -145,12 +187,12 @@ enum { + }; + + #define F_PARSE (1<<0) /* process modem status messages */ +-#define F_ISSUE (1<<1) /* display /etc/issue */ ++#define F_ISSUE (1<<1) /* display /etc/issue or /etc/issue.d */ + #define F_RTSCTS (1<<2) /* enable RTS/CTS flow control */ + + #define F_INITSTRING (1<<4) /* initstring is set */ + #define F_WAITCRLF (1<<5) /* wait for CR or LF */ +-#define F_CUSTISSUE (1<<6) /* give alternative issue file */ ++ + #define F_NOPROMPT (1<<7) /* do not ask for login name! */ + #define F_LCUC (1<<8) /* support for *LCUC stty modes */ + #define F_KEEPSPEED (1<<9) /* follow baud rate from kernel */ +@@ -189,16 +231,14 @@ static const struct Speedtab speedtab[] = { + {2400, B2400}, + {4800, B4800}, + {9600, B9600}, +-#ifdef B19200 ++#ifdef B19200 + {19200, B19200}, +-#endif +-#ifdef B38400 +- {38400, B38400}, +-#endif +-#ifdef EXTA ++#elif defined(EXTA) + {19200, EXTA}, + #endif +-#ifdef EXTB ++#ifdef B38400 ++ {38400, B38400}, ++#elif defined(EXTB) + {38400, EXTB}, + #endif + #ifdef B57600 +@@ -209,6 +249,42 @@ static const struct Speedtab speedtab[] = { + #endif + #ifdef B230400 + {230400, B230400}, ++#endif ++#ifdef B460800 ++ {460800, B460800}, ++#endif ++#ifdef B500000 ++ {500000, B500000}, ++#endif ++#ifdef B576000 ++ {576000, B576000}, ++#endif ++#ifdef B921600 ++ {921600, B921600}, ++#endif ++#ifdef B1000000 ++ {1000000, B1000000}, ++#endif ++#ifdef B1152000 ++ {1152000, B1152000}, ++#endif ++#ifdef B1500000 ++ {1500000, B1500000}, ++#endif ++#ifdef B2000000 ++ {2000000, B2000000}, ++#endif ++#ifdef B2500000 ++ {2500000, B2500000}, ++#endif ++#ifdef B3000000 ++ {3000000, B3000000}, ++#endif ++#ifdef B3500000 ++ {3500000, B3500000}, ++#endif ++#ifdef B4000000 ++ {4000000, B4000000}, + #endif + {0, 0}, + }; +@@ -221,6 +297,7 @@ static void open_tty(char *tty, struct termios *tp, struct options *op); + static void termio_init(struct options *op, struct termios *tp); + static void reset_vc (const struct options *op, struct termios *tp); + static void auto_baud(struct termios *tp); ++static void list_speeds(void); + static void output_special_char (unsigned char c, struct options *op, + struct termios *tp, FILE *fp); + static void do_prompt(struct options *op, struct termios *tp); +@@ -231,7 +308,8 @@ static void termio_final(struct options *op, + struct termios *tp, struct chardata *cp); + static int caps_lock(char *s); + static speed_t bcode(char *s); +-static void usage(FILE * out) __attribute__((__noreturn__)); ++static void usage(void) __attribute__((__noreturn__)); ++static void exit_slowly(int code) __attribute__((__noreturn__)); + static void log_err(const char *, ...) __attribute__((__noreturn__)) + __attribute__((__format__(printf, 1, 2))); + static void log_warn (const char *, ...) +@@ -239,13 +317,16 @@ static void log_warn (const char *, ...) + static ssize_t append(char *dest, size_t len, const char *sep, const char *src); + static void check_username (const char* nm); + static void login_options_to_argv(char *argv[], int *argc, char *str, char *username); ++static void reload_agettys(void); ++static void print_issue_file(struct options *op, struct termios *tp); + + /* Fake hostname for ut_host specified on command line. */ + static char *fakehost; + + #ifdef DEBUGGING ++# include "closestream.h" + # ifndef DEBUG_OUTPUT +-# define DEBUG_OUTPUT "/dev/ttyp0" ++# define DEBUG_OUTPUT "/dev/tty10" + # endif + # define debug(s) do { fprintf(dbf,s); fflush(dbf); } while (0) + FILE *dbf; +@@ -261,8 +342,7 @@ int main(int argc, char **argv) + struct options options = { + .flags = F_ISSUE, /* show /etc/issue (SYSV_STYLE) */ + .login = _PATH_LOGIN, /* default login program */ +- .tty = "tty1", /* default tty line */ +- .issue = ISSUE /* default issue file */ ++ .tty = "tty1" /* default tty line */ + }; + char *login_argv[LOGIN_ARGV_MAX + 1]; + int login_argc = 0; +@@ -283,8 +363,12 @@ int main(int argc, char **argv) + + #ifdef DEBUGGING + dbf = fopen(DEBUG_OUTPUT, "w"); +- for (int i = 1; i < argc; i++) ++ for (int i = 1; i < argc; i++) { ++ if (i > 1) ++ debug(" "); + debug(argv[i]); ++ } ++ debug("\n"); + #endif /* DEBUGGING */ + + /* Parse command-line arguments. */ +@@ -311,6 +395,13 @@ int main(int argc, char **argv) + sigaction(SIGHUP, &sa_hup, NULL); + + tcsetpgrp(STDIN_FILENO, getpid()); ++ ++ /* Default is to follow the current line speed and then default to 9600 */ ++ if ((options.flags & F_VCONSOLE) == 0 && options.numspeed == 0) { ++ options.speeds[options.numspeed++] = bcode("9600"); ++ options.flags |= F_KEEPSPEED; ++ } ++ + /* Initialize the termios settings (raw mode, eight-bit, blocking i/o). */ + debug("calling termio_init\n"); + termio_init(&options, &termios); +@@ -337,7 +428,7 @@ int main(int argc, char **argv) + + /* Set the optional timer. */ + if (options.timeout) +- alarm((unsigned) options.timeout); ++ alarm(options.timeout); + + /* Optionally wait for CR or LF before writing /etc/issue */ + if (serial_tty_option(&options, F_WAITCRLF)) { +@@ -362,7 +453,9 @@ int main(int argc, char **argv) + username = options.autolog; + } + +- if ((options.flags & F_NOPROMPT) == 0) { ++ if (options.flags & F_NOPROMPT) { /* --skip-login */ ++ print_issue_file(&options, &termios); ++ } else { /* regular (auto)login */ + if (options.autolog) { + /* Autologin prompt */ + do_prompt(&options, &termios); +@@ -372,7 +465,7 @@ int main(int argc, char **argv) + debug("reading login name\n"); + while ((username = + get_logname(&options, &termios, &chardata)) == NULL) +- if ((options.flags & F_VCONSOLE) == 0) ++ if ((options.flags & F_VCONSOLE) == 0 && options.numspeed) + next_speed(&options, &termios); + } + } +@@ -403,9 +496,12 @@ int main(int argc, char **argv) + login_options_to_argv(login_argv, &login_argc, + options.logopt, username); + } else { +- if (fakehost && (options.flags & F_REMOTE)) { +- login_argv[login_argc++] = "-h"; +- login_argv[login_argc++] = fakehost; ++ if (options.flags & F_REMOTE) { ++ if (fakehost) { ++ login_argv[login_argc++] = "-h"; ++ login_argv[login_argc++] = fakehost; ++ } else if (options.flags & F_NOHOSTNAME) ++ login_argv[login_argc++] = "-H"; + } + if (username) { + if (options.autolog) +@@ -418,25 +514,18 @@ int main(int argc, char **argv) + + login_argv[login_argc] = NULL; /* last login argv */ + +- if (options.chroot) { +- if (chroot(options.chroot) < 0) +- log_err(_("%s: can't change root directory %s: %m"), +- options.tty, options.chroot); +- } +- if (options.chdir) { +- if (chdir(options.chdir) < 0) +- log_err(_("%s: can't change working directory %s: %m"), +- options.tty, options.chdir); +- } +- if (options.nice) { +- if (nice(options.nice) < 0) +- log_warn(_("%s: can't change process priority: %m"), +- options.tty); +- } +- if (options.osrelease) +- free(options.osrelease); ++ if (options.chroot && chroot(options.chroot) < 0) ++ log_err(_("%s: can't change root directory %s: %m"), ++ options.tty, options.chroot); ++ if (options.chdir && chdir(options.chdir) < 0) ++ log_err(_("%s: can't change working directory %s: %m"), ++ options.tty, options.chdir); ++ if (options.nice && nice(options.nice) < 0) ++ log_warn(_("%s: can't change process priority: %m"), ++ options.tty); ++ ++ free(options.osrelease); + #ifdef DEBUGGING +- fprintf(dbf, "read %c\n", ch); + if (close_stream(dbf) != 0) + log_err("write failed: %s", DEBUG_OUTPUT); + #endif +@@ -477,7 +566,7 @@ static char *replace_u(char *str, char *username) + log_err(_("failed to allocate memory: %m")); + + if (p != str) { +- /* copy chars befor \u */ ++ /* copy chars before \u */ + memcpy(tp, str, p - str); + tp += p - str; + } +@@ -527,6 +616,55 @@ static void login_options_to_argv(char *argv[], int *argc, + *argc = i; + } + ++static void output_version(void) ++{ ++ static const char *features[] = { ++#ifdef DEBUGGING ++ "debug", ++#endif ++#ifdef CRTSCTS ++ "flow control", ++#endif ++#ifdef KDGKBLED ++ "hints", ++#endif ++#ifdef ISSUE_SUPPORT ++ "issue", ++#endif ++#ifdef ISSUEDIR_SUPPORT ++ "issue.d", ++#endif ++#ifdef KDGKBMODE ++ "keyboard mode", ++#endif ++#ifdef USE_PLYMOUTH_SUPPORT ++ "plymouth", ++#endif ++#ifdef AGETTY_RELOAD ++ "reload", ++#endif ++#ifdef USE_SYSLOG ++ "syslog", ++#endif ++#ifdef HAVE_WIDECHAR ++ "widechar", ++#endif ++ NULL ++ }; ++ unsigned int i; ++ ++ printf( _("%s from %s"), program_invocation_short_name, PACKAGE_STRING); ++ fputs(" (", stdout); ++ for (i = 0; features[i]; i++) { ++ if (0 < i) ++ fputs(", ", stdout); ++ printf("%s", features[i]); ++ } ++ fputs(")\n", stdout); ++} ++ ++#define is_speed(str) (strlen((str)) == strspn((str), "0123456789,")) ++ + /* Parse command-line arguments. */ + static void parse_args(int argc, char **argv, struct options *op) + { +@@ -540,46 +678,50 @@ static void parse_args(int argc, char **argv, struct options *op) + HELP_OPTION, + ERASE_CHARS_OPTION, + KILL_CHARS_OPTION, ++ RELOAD_OPTION, ++ LIST_SPEEDS_OPTION, + }; + const struct option longopts[] = { +- { "8bits", no_argument, 0, '8' }, +- { "autologin", required_argument, 0, 'a' }, +- { "noreset", no_argument, 0, 'c' }, +- { "chdir", required_argument, 0, 'C' }, +- { "delay", required_argument, 0, 'd' }, +- { "remote", no_argument, 0, 'E' }, +- { "issue-file", required_argument, 0, 'f' }, +- { "flow-control", no_argument, 0, 'h' }, +- { "host", required_argument, 0, 'H' }, +- { "noissue", no_argument, 0, 'i' }, +- { "init-string", required_argument, 0, 'I' }, +- { "noclear", no_argument, 0, 'J' }, +- { "login-program", required_argument, 0, 'l' }, +- { "local-line", optional_argument, 0, 'L' }, +- { "extract-baud", no_argument, 0, 'm' }, +- { "skip-login", no_argument, 0, 'n' }, +- { "nonewline", no_argument, 0, 'N' }, +- { "login-options", required_argument, 0, 'o' }, +- { "login-pause", no_argument, 0, 'p' }, +- { "nice", required_argument, 0, 'P' }, +- { "chroot", required_argument, 0, 'r' }, +- { "hangup", no_argument, 0, 'R' }, +- { "keep-baud", no_argument, 0, 's' }, +- { "timeout", required_argument, 0, 't' }, +- { "detect-case", no_argument, 0, 'U' }, +- { "wait-cr", no_argument, 0, 'w' }, +- { "nohints", no_argument, 0, NOHINTS_OPTION }, +- { "nohostname", no_argument, 0, NOHOSTNAME_OPTION }, +- { "long-hostname", no_argument, 0, LONGHOSTNAME_OPTION }, +- { "version", no_argument, 0, VERSION_OPTION }, +- { "help", no_argument, 0, HELP_OPTION }, +- { "erase-chars", required_argument, 0, ERASE_CHARS_OPTION }, +- { "kill-chars", required_argument, 0, KILL_CHARS_OPTION }, +- { NULL, 0, 0, 0 } ++ { "8bits", no_argument, NULL, '8' }, ++ { "autologin", required_argument, NULL, 'a' }, ++ { "noreset", no_argument, NULL, 'c' }, ++ { "chdir", required_argument, NULL, 'C' }, ++ { "delay", required_argument, NULL, 'd' }, ++ { "remote", no_argument, NULL, 'E' }, ++ { "issue-file", required_argument, NULL, 'f' }, ++ { "flow-control", no_argument, NULL, 'h' }, ++ { "host", required_argument, NULL, 'H' }, ++ { "noissue", no_argument, NULL, 'i' }, ++ { "init-string", required_argument, NULL, 'I' }, ++ { "noclear", no_argument, NULL, 'J' }, ++ { "login-program", required_argument, NULL, 'l' }, ++ { "local-line", optional_argument, NULL, 'L' }, ++ { "extract-baud", no_argument, NULL, 'm' }, ++ { "list-speeds", no_argument, NULL, LIST_SPEEDS_OPTION }, ++ { "skip-login", no_argument, NULL, 'n' }, ++ { "nonewline", no_argument, NULL, 'N' }, ++ { "login-options", required_argument, NULL, 'o' }, ++ { "login-pause", no_argument, NULL, 'p' }, ++ { "nice", required_argument, NULL, 'P' }, ++ { "chroot", required_argument, NULL, 'r' }, ++ { "hangup", no_argument, NULL, 'R' }, ++ { "keep-baud", no_argument, NULL, 's' }, ++ { "timeout", required_argument, NULL, 't' }, ++ { "detect-case", no_argument, NULL, 'U' }, ++ { "wait-cr", no_argument, NULL, 'w' }, ++ { "nohints", no_argument, NULL, NOHINTS_OPTION }, ++ { "nohostname", no_argument, NULL, NOHOSTNAME_OPTION }, ++ { "long-hostname", no_argument, NULL, LONGHOSTNAME_OPTION }, ++ { "reload", no_argument, NULL, RELOAD_OPTION }, ++ { "version", no_argument, NULL, VERSION_OPTION }, ++ { "help", no_argument, NULL, HELP_OPTION }, ++ { "erase-chars", required_argument, NULL, ERASE_CHARS_OPTION }, ++ { "kill-chars", required_argument, NULL, KILL_CHARS_OPTION }, ++ { NULL, 0, NULL, 0 } + }; + + while ((c = getopt_long(argc, argv, +- "8a:cC:d:Ef:hH:iI:Jl:LmnNo:pP:r:Rst:Uw", longopts, ++ "8a:cC:d:Ef:hH:iI:Jl:L::mnNo:pP:r:Rst:Uw", longopts, + NULL)) != -1) { + switch (c) { + case '8': +@@ -595,13 +737,12 @@ static void parse_args(int argc, char **argv, struct options *op) + op->chdir = optarg; + break; + case 'd': +- op->delay = atoi(optarg); ++ op->delay = strtou32_or_err(optarg, _("invalid delay argument")); + break; + case 'E': + op->flags |= F_REMOTE; + break; + case 'f': +- op->flags |= F_CUSTISSUE; + op->issue = optarg; + break; + case 'h': +@@ -634,7 +775,7 @@ static void parse_args(int argc, char **argv, struct options *op) + else if (strcmp(optarg, "=auto") == 0) + op->clocal = CLOCAL_MODE_AUTO; + else +- log_err(_("unssuported --local-line mode argument")); ++ log_err(_("invalid argument of --local-line")); + } + break; + case 'm': +@@ -643,6 +784,9 @@ static void parse_args(int argc, char **argv, struct options *op) + case 'n': + op->flags |= F_NOPROMPT; + break; ++ case 'N': ++ op->flags |= F_NONL; ++ break; + case 'o': + op->logopt = optarg; + break; +@@ -650,7 +794,7 @@ static void parse_args(int argc, char **argv, struct options *op) + op->flags |= F_LOGINPAUSE; + break; + case 'P': +- op->nice = atoi(optarg); ++ op->nice = strtos32_or_err(optarg, _("invalid nice argument")); + break; + case 'r': + op->chroot = optarg; +@@ -662,8 +806,7 @@ static void parse_args(int argc, char **argv, struct options *op) + op->flags |= F_KEEPSPEED; + break; + case 't': +- if ((op->timeout = atoi(optarg)) <= 0) +- log_err(_("bad timeout value: %s"), optarg); ++ op->timeout = strtou32_or_err(optarg, _("invalid timeout argument")); + break; + case 'U': + op->flags |= F_LCUC; +@@ -686,14 +829,19 @@ static void parse_args(int argc, char **argv, struct options *op) + case KILL_CHARS_OPTION: + op->killchars = optarg; + break; ++ case RELOAD_OPTION: ++ reload_agettys(); ++ exit(EXIT_SUCCESS); ++ case LIST_SPEEDS_OPTION: ++ list_speeds(); ++ exit(EXIT_SUCCESS); + case VERSION_OPTION: +- printf(_("%s from %s\n"), program_invocation_short_name, +- PACKAGE_STRING); ++ output_version(); + exit(EXIT_SUCCESS); + case HELP_OPTION: +- usage(stdout); ++ usage(); + default: +- usage(stderr); ++ errtryhelp(EXIT_FAILURE); + } + } + +@@ -701,26 +849,26 @@ static void parse_args(int argc, char **argv, struct options *op) + + if (argc < optind + 1) { + log_warn(_("not enough arguments")); +- usage(stderr); ++ errx(EXIT_FAILURE, _("not enough arguments")); + } + + /* Accept "tty", "baudrate tty", and "tty baudrate". */ +- if ('0' <= argv[optind][0] && argv[optind][0] <= '9') { ++ if (is_speed(argv[optind])) { + /* Assume BSD style speed. */ + parse_speeds(op, argv[optind++]); + if (argc < optind + 1) { +- warn(_("not enough arguments")); +- usage(stderr); ++ log_warn(_("not enough arguments")); ++ errx(EXIT_FAILURE, _("not enough arguments")); + } + op->tty = argv[optind++]; + } else { + op->tty = argv[optind++]; + if (argc > optind) { +- char *v = argv[optind++]; +- if ('0' <= *v && *v <= '9') ++ char *v = argv[optind]; ++ if (is_speed(v)) { + parse_speeds(op, v); +- else +- op->speeds[op->numspeed++] = bcode("9600"); ++ optind++; ++ } + } + } + +@@ -732,45 +880,6 @@ static void parse_args(int argc, char **argv, struct options *op) + if (argc > optind && argv[optind]) + op->term = argv[optind]; + +-#ifdef DO_DEVFS_FIDDLING +- /* +- * Some devfs junk, following Goswin Brederlow: +- * turn ttyS into tts/ +- * turn tty into vc/ +- * http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=72241 +- */ +- if (op->tty && strlen(op->tty) < 90) { +- char dev_name[100]; +- struct stat st; +- +- if (strncmp(op->tty, "ttyS", 4) == 0) { +- strcpy(dev_name, "/dev/"); +- strcat(dev_name, op->tty); +- if (stat(dev_name, &st) < 0) { +- strcpy(dev_name, "/dev/tts/"); +- strcat(dev_name, op->tty + 4); +- if (stat(dev_name, &st) == 0) { +- op->tty = strdup(dev_name + 5); +- if (!op->tty) +- log_err(_("failed to allocate memory: %m")); +- } +- } +- } else if (strncmp(op->tty, "tty", 3) == 0) { +- strcpy(dev_name, "/dev/"); +- strncat(dev_name, op->tty, 90); +- if (stat(dev_name, &st) < 0) { +- strcpy(dev_name, "/dev/vc/"); +- strcat(dev_name, op->tty + 3); +- if (stat(dev_name, &st) == 0) { +- op->tty = strdup(dev_name + 5); +- if (!op->tty) +- log_err(_("failed to allocate memory: %m")); +- } +- } +- } +- } +-#endif /* DO_DEVFS_FIDDLING */ +- + debug("exiting parseargs\n"); + } + +@@ -778,15 +887,20 @@ static void parse_args(int argc, char **argv, struct options *op) + static void parse_speeds(struct options *op, char *arg) + { + char *cp; ++ char *str = strdup(arg); ++ ++ if (!str) ++ log_err(_("failed to allocate memory: %m")); + +- debug("entered parse_speeds\n"); +- for (cp = strtok(arg, ","); cp != NULL; cp = strtok((char *)0, ",")) { ++ debug("entered parse_speeds:\n"); ++ for (cp = strtok(str, ","); cp != NULL; cp = strtok((char *)0, ",")) { + if ((op->speeds[op->numspeed++] = bcode(cp)) <= 0) + log_err(_("bad speed: %s"), cp); + if (op->numspeed >= MAX_SPEED) + log_err(_("too many alternate speeds")); + } + debug("exiting parsespeeds\n"); ++ free(str); + } + + #ifdef SYSV_STYLE +@@ -794,13 +908,13 @@ static void parse_speeds(struct options *op, char *arg) + /* Update our utmp entry. */ + static void update_utmp(struct options *op) + { +- struct utmp ut; ++ struct utmpx ut; + time_t t; + pid_t pid = getpid(); + pid_t sid = getsid(0); + char *vcline = op->vcline; + char *line = op->tty; +- struct utmp *utp; ++ struct utmpx *utp; + + /* + * The utmp file holds miscellaneous information about things started by +@@ -810,8 +924,8 @@ static void update_utmp(struct options *op) + * utmp file can be opened for update, and if we are able to find our + * entry in the utmp file. + */ +- utmpname(_PATH_UTMP); +- setutent(); ++ utmpxname(_PATH_UTMP); ++ setutxent(); + + /* + * Find my pid in utmp. +@@ -822,7 +936,7 @@ static void update_utmp(struct options *op) + * FIXME: The present code is taken from login.c, so if this is changed, + * maybe login has to be changed as well (is this true?). + */ +- while ((utp = getutent())) ++ while ((utp = getutxent())) + if (utp->ut_pid == pid + && utp->ut_type >= INIT_PROCESS + && utp->ut_type <= DEAD_PROCESS) +@@ -852,33 +966,15 @@ static void update_utmp(struct options *op) + if (fakehost) + strncpy(ut.ut_host, fakehost, sizeof(ut.ut_host)); + time(&t); +- ut.ut_time = t; ++ ut.ut_tv.tv_sec = t; + ut.ut_type = LOGIN_PROCESS; + ut.ut_pid = pid; + ut.ut_session = sid; + +- pututline(&ut); +- endutent(); ++ pututxline(&ut); ++ endutxent(); + +- { +-#ifdef HAVE_UPDWTMP +- updwtmp(_PATH_WTMP, &ut); +-#else +- int ut_fd; +- int lf; +- +- if ((lf = open(_PATH_WTMPLOCK, O_CREAT | O_WRONLY, 0660)) >= 0) { +- flock(lf, LOCK_EX); +- if ((ut_fd = +- open(_PATH_WTMP, O_APPEND | O_WRONLY)) >= 0) { +- write_all(ut_fd, &ut, sizeof(ut)); +- close(ut_fd); +- } +- flock(lf, LOCK_UN); +- close(lf); +- } +-#endif /* HAVE_UPDWTMP */ +- } ++ updwtmpx(_PATH_WTMP, &ut); + } + + #endif /* SYSV_STYLE */ +@@ -888,6 +984,9 @@ static void open_tty(char *tty, struct termios *tp, struct options *op) + { + const pid_t pid = getpid(); + int closed = 0; ++#ifndef KDGKBMODE ++ int serial; ++#endif + + /* Set up new standard input, unless we are given an already opened port. */ + +@@ -903,8 +1002,12 @@ static void open_tty(char *tty, struct termios *tp, struct options *op) + if ((gr = getgrnam("tty"))) + gid = gr->gr_gid; + +- if (((len = snprintf(buf, sizeof(buf), "/dev/%s", tty)) >= +- (int)sizeof(buf)) || (len < 0)) ++ len = snprintf(buf, sizeof(buf), "/dev/%s", tty); ++ if (len < 0 || (size_t)len >= sizeof(buf)) ++ log_err(_("/dev/%s: cannot open as standard input: %m"), tty); ++ ++ /* Open the tty as standard input. */ ++ if ((fd = open(buf, O_RDWR|O_NOCTTY|O_NONBLOCK, 0)) < 0) + log_err(_("/dev/%s: cannot open as standard input: %m"), tty); + + /* +@@ -913,24 +1016,20 @@ static void open_tty(char *tty, struct termios *tp, struct options *op) + * Linux login(1) will change tty permissions. Use root owner and group + * with permission -rw------- for the period between getty and login. + */ +- if (chown(buf, 0, gid) || chmod(buf, (gid ? 0620 : 0600))) { ++ if (fchown(fd, 0, gid) || fchmod(fd, (gid ? 0620 : 0600))) { + if (errno == EROFS) + log_warn("%s: %m", buf); + else + log_err("%s: %m", buf); + } + +- /* Open the tty as standard input. */ +- if ((fd = open(buf, O_RDWR|O_NOCTTY|O_NONBLOCK, 0)) < 0) +- log_err(_("/dev/%s: cannot open as standard input: %m"), tty); +- + /* Sanity checks... */ +- if (!isatty(fd)) +- log_err(_("/dev/%s: not a character device"), tty); + if (fstat(fd, &st) < 0) + log_err("%s: %m", buf); + if ((st.st_mode & S_IFMT) != S_IFCHR) + log_err(_("/dev/%s: not a character device"), tty); ++ if (!isatty(fd)) ++ log_err(_("/dev/%s: not a tty"), tty); + + if (((tid = tcgetsid(fd)) < 0) || (pid != tid)) { + if (ioctl(fd, TIOCSCTTY, 1) == -1) +@@ -946,7 +1045,7 @@ static void open_tty(char *tty, struct termios *tp, struct options *op) + debug("TIOCNOTTY ioctl failed\n"); + + /* +- * Let's close all file decriptors before vhangup ++ * Let's close all file descriptors before vhangup + * https://lkml.org/lkml/2012/6/5/145 + */ + close(fd); +@@ -1015,22 +1114,66 @@ static void open_tty(char *tty, struct termios *tp, struct options *op) + if (tcgetattr(STDIN_FILENO, tp) < 0) + log_err(_("%s: failed to get terminal attributes: %m"), tty); + ++#if defined (__s390__) || defined (__s390x__) ++ if (!op->term) { ++ /* ++ * Special terminal on first serial line on a S/390(x) which ++ * is due legacy reasons a block terminal of type 3270 or ++ * higher. Whereas the second serial line on a S/390(x) is ++ * a real character terminal which is compatible with VT220. ++ */ ++ if (strcmp(op->tty, "ttyS0") == 0) /* linux/drivers/s390/char/con3215.c */ ++ op->term = DEFAULT_TTYS0; ++ else if (strncmp(op->tty, "3270/tty", 8) == 0) /* linux/drivers/s390/char/con3270.c */ ++ op->term = DEFAULT_TTY32; ++ else if (strcmp(op->tty, "ttyS1") == 0) /* linux/drivers/s390/char/sclp_vt220.c */ ++ op->term = DEFAULT_TTYS1; ++ } ++#endif ++ ++#if defined(__FreeBSD_kernel__) ++ login_tty (0); ++#endif ++ + /* + * Detect if this is a virtual console or serial/modem line. + * In case of a virtual console the ioctl KDGKBMODE succeeds + * whereas on other lines it will fails. + */ +- if (ioctl(STDIN_FILENO, KDGKBMODE, &op->kbmode) == 0) { ++#ifdef KDGKBMODE ++ if (ioctl(STDIN_FILENO, KDGKBMODE, &op->kbmode) == 0) ++#else ++ if (ioctl(STDIN_FILENO, TIOCMGET, &serial) < 0 && (errno == EINVAL)) ++#endif ++ { + op->flags |= F_VCONSOLE; + if (!op->term) + op->term = DEFAULT_VCTERM; + } else { ++#ifdef K_RAW + op->kbmode = K_RAW; ++#endif + if (!op->term) + op->term = DEFAULT_STERM; + } + +- setenv("TERM", op->term, 1); ++ if (setenv("TERM", op->term, 1) != 0) ++ log_err(_("failed to set the %s environment variable"), "TERM"); ++} ++ ++/* Initialize termios settings. */ ++static void termio_clear(int fd) ++{ ++ /* ++ * Do not write a full reset (ESC c) because this destroys ++ * the unicode mode again if the terminal was in unicode ++ * mode. Also it clears the CONSOLE_MAGIC features which ++ * are required for some languages/console-fonts. ++ * Just put the cursor to the home position (ESC [ H), ++ * erase everything below the cursor (ESC [ J), and set the ++ * scrolling region to the full window (ESC [ r) ++ */ ++ write_all(fd, "\033[r\033[H\033[J", 9); + } + + /* Initialize termios settings. */ +@@ -1038,6 +1181,28 @@ static void termio_init(struct options *op, struct termios *tp) + { + speed_t ispeed, ospeed; + struct winsize ws; ++#ifdef USE_PLYMOUTH_SUPPORT ++ struct termios lock; ++ int i = (plymouth_command(MAGIC_PING) == 0) ? PLYMOUTH_TERMIOS_FLAGS_DELAY : 0; ++ if (i) ++ plymouth_command(MAGIC_QUIT); ++ while (i-- > 0) { ++ /* ++ * Even with TTYReset=no it seems with systemd or plymouth ++ * the termios flags become changed from under the first ++ * agetty on a serial system console as the flags are locked. ++ */ ++ memset(&lock, 0, sizeof(struct termios)); ++ if (ioctl(STDIN_FILENO, TIOCGLCKTRMIOS, &lock) < 0) ++ break; ++ if (!lock.c_iflag && !lock.c_oflag && !lock.c_cflag && !lock.c_lflag) ++ break; ++ debug("termios locked\n"); ++ sleep(1); ++ } ++ memset(&lock, 0, sizeof(struct termios)); ++ ioctl(STDIN_FILENO, TIOCSLCKTRMIOS, &lock); ++#endif + + if (op->flags & F_VCONSOLE) { + #if defined(IUTF8) && defined(KDGKBMODE) +@@ -1063,22 +1228,16 @@ static void termio_init(struct options *op, struct termios *tp) + if ((tp->c_cflag & (CS8|PARODD|PARENB)) == CS8) + op->flags |= F_EIGHTBITS; + +- if ((op->flags & F_NOCLEAR) == 0) { +- /* +- * Do not write a full reset (ESC c) because this destroys +- * the unicode mode again if the terminal was in unicode +- * mode. Also it clears the CONSOLE_MAGIC features which +- * are required for some languages/console-fonts. +- * Just put the cursor to the home position (ESC [ H), +- * erase everything below the cursor (ESC [ J), and set the +- * scrolling region to the full window (ESC [ r) +- */ +- write_all(STDOUT_FILENO, "\033[r\033[H\033[J", 9); +- } ++ if ((op->flags & F_NOCLEAR) == 0) ++ termio_clear(STDOUT_FILENO); + return; + } + +- if (op->flags & F_KEEPSPEED) { ++ /* ++ * Serial line ++ */ ++ ++ if (op->flags & F_KEEPSPEED || !op->numspeed) { + /* Save the original setting. */ + ispeed = cfgetispeed(tp); + ospeed = cfgetospeed(tp); +@@ -1097,9 +1256,6 @@ static void termio_init(struct options *op, struct termios *tp) + * later on. + */ + +- /* Flush input and output queues, important for modems! */ +- tcflush(STDIN_FILENO, TCIOFLUSH); +- + /* The defaul is set c_iflag in termio_final() according to chardata. + * Unfortunately, the chardata are not set according to the serial line + * if --autolog is enabled. In this case we do not read from the line +@@ -1124,7 +1280,7 @@ static void termio_init(struct options *op, struct termios *tp) + + /* + * Note that the speed is stored in the c_cflag termios field, so we have +- * set the speed always when the cflag se reseted. ++ * set the speed always when the cflag is reset. + */ + cfsetispeed(tp, ispeed); + cfsetospeed(tp, ospeed); +@@ -1150,15 +1306,10 @@ static void termio_init(struct options *op, struct termios *tp) + + /* Check for terminal size and if not found set default */ + if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == 0) { +- int set = 0; +- if (ws.ws_row == 0) { ++ if (ws.ws_row == 0) + ws.ws_row = 24; +- set++; +- } +- if (ws.ws_col == 0) { ++ if (ws.ws_col == 0) + ws.ws_col = 80; +- set++; +- } + if (ioctl(STDIN_FILENO, TIOCSWINSZ, &ws)) + debug("TIOCSWINSZ ioctl failed\n"); + } +@@ -1168,8 +1319,11 @@ static void termio_init(struct options *op, struct termios *tp) + if (op->flags & F_RTSCTS) + tp->c_cflag |= CRTSCTS; + #endif ++ /* Flush input and output queues, important for modems! */ ++ tcflush(STDIN_FILENO, TCIOFLUSH); + +- tcsetattr(STDIN_FILENO, TCSANOW, tp); ++ if (tcsetattr(STDIN_FILENO, TCSANOW, tp)) ++ log_warn(_("setting terminal attributes failed: %m")); + + /* Go to blocking input even in local mode. */ + fcntl(STDIN_FILENO, F_SETFL, +@@ -1190,6 +1344,10 @@ static void reset_vc(const struct options *op, struct termios *tp) + + if (tcsetattr(STDIN_FILENO, TCSADRAIN, tp)) + log_warn(_("setting terminal attributes failed: %m")); ++ ++ /* Go to blocking input even in local mode. */ ++ fcntl(STDIN_FILENO, F_SETFL, ++ fcntl(STDIN_FILENO, F_GETFL, 0) & ~O_NONBLOCK); + } + + /* Extract baud rate from modem status message. */ +@@ -1273,7 +1431,7 @@ static char *xgetdomainname(void) + { + #ifdef HAVE_GETDOMAINNAME + char *name; +- size_t sz = get_hostname_max() + 1; ++ const size_t sz = get_hostname_max() + 1; + + name = malloc(sizeof(char) * sz); + if (!name) +@@ -1285,8 +1443,9 @@ static char *xgetdomainname(void) + } + name[sz - 1] = '\0'; + return name; +-#endif ++#else + return NULL; ++#endif + } + + +@@ -1299,10 +1458,13 @@ static char *read_os_release(struct options *op, const char *varname) + + /* read the file only once */ + if (!op->osrelease) { +- fd = open(_PATH_OS_RELEASE, O_RDONLY); ++ fd = open(_PATH_OS_RELEASE_ETC, O_RDONLY); + if (fd == -1) { +- log_warn(_("cannot open: %s: %m"), _PATH_OS_RELEASE); +- return NULL; ++ fd = open(_PATH_OS_RELEASE_USR, O_RDONLY); ++ if (fd == -1) { ++ log_warn(_("cannot open os-release file")); ++ return NULL; ++ } + } + + if (fstat(fd, &st) < 0 || st.st_size > 4 * 1024 * 1024) +@@ -1359,8 +1521,7 @@ static char *read_os_release(struct options *op, const char *varname) + } + break; + } +- if (ret) +- free(ret); ++ free(ret); + ret = strdup(p); + if (!ret) + log_err(_("failed to allocate memory: %m")); +@@ -1373,20 +1534,251 @@ done: + return ret; + } + +-/* Show login prompt, optionally preceded by /etc/issue contents. */ +-static void do_prompt(struct options *op, struct termios *tp) ++#ifdef AGETTY_RELOAD ++static void open_netlink(void) + { +-#ifdef ISSUE +- FILE *fd; +-#endif /* ISSUE */ ++ struct sockaddr_nl addr = { 0, }; ++ int sock; + ++ if (netlink_fd != AGETTY_RELOAD_FDNONE) ++ return; ++ ++ sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); ++ if (sock >= 0) { ++ addr.nl_family = AF_NETLINK; ++ addr.nl_pid = getpid(); ++ addr.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR; ++ if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) ++ close(sock); ++ else ++ netlink_fd = sock; ++ } ++} ++ ++static int process_netlink_msg(int *changed) ++{ ++ char buf[4096]; ++ struct sockaddr_nl snl; ++ struct nlmsghdr *h; ++ int rc; ++ ++ struct iovec iov = { ++ .iov_base = buf, ++ .iov_len = sizeof(buf) ++ }; ++ struct msghdr msg = { ++ .msg_name = &snl, ++ .msg_namelen = sizeof(snl), ++ .msg_iov = &iov, ++ .msg_iovlen = 1, ++ .msg_control = NULL, ++ .msg_controllen = 0, ++ .msg_flags = 0 ++ }; ++ ++ rc = recvmsg(netlink_fd, &msg, MSG_DONTWAIT); ++ if (rc < 0) { ++ if (errno == EWOULDBLOCK || errno == EAGAIN) ++ return 0; ++ ++ /* Failure, just stop listening for changes */ ++ close(netlink_fd); ++ netlink_fd = AGETTY_RELOAD_FDNONE; ++ return 0; ++ } ++ ++ for (h = (struct nlmsghdr *)buf; NLMSG_OK(h, (unsigned int)rc); h = NLMSG_NEXT(h, rc)) { ++ if (h->nlmsg_type == NLMSG_DONE || ++ h->nlmsg_type == NLMSG_ERROR) { ++ close(netlink_fd); ++ netlink_fd = AGETTY_RELOAD_FDNONE; ++ return 0; ++ } ++ ++ *changed = 1; ++ break; ++ } ++ ++ return 1; ++} ++ ++static int process_netlink(void) ++{ ++ int changed = 0; ++ while (process_netlink_msg(&changed)); ++ return changed; ++} ++ ++static int wait_for_term_input(int fd) ++{ ++ char buffer[sizeof(struct inotify_event) + NAME_MAX + 1]; ++ fd_set rfds; ++ ++ if (inotify_fd == AGETTY_RELOAD_FDNONE) { ++ /* make sure the reload trigger file exists */ ++ int reload_fd = open(AGETTY_RELOAD_FILENAME, ++ O_CREAT|O_CLOEXEC|O_RDONLY, ++ S_IRUSR|S_IWUSR); ++ ++ /* initialize reload trigger inotify stuff */ ++ if (reload_fd >= 0) { ++ inotify_fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); ++ if (inotify_fd > 0) ++ inotify_add_watch(inotify_fd, AGETTY_RELOAD_FILENAME, ++ IN_ATTRIB | IN_MODIFY); ++ ++ close(reload_fd); ++ } else ++ log_warn(_("failed to create reload file: %s: %m"), ++ AGETTY_RELOAD_FILENAME); ++ } ++ ++ while (1) { ++ int nfds = fd; ++ ++ FD_ZERO(&rfds); ++ FD_SET(fd, &rfds); ++ ++ if (inotify_fd >= 0) { ++ FD_SET(inotify_fd, &rfds); ++ nfds = max(nfds, inotify_fd); ++ } ++ if (netlink_fd >= 0) { ++ FD_SET(netlink_fd, &rfds); ++ nfds = max(nfds, netlink_fd); ++ } ++ ++ /* If waiting fails, just fall through, presumably reading input will fail */ ++ if (select(nfds + 1, &rfds, NULL, NULL, NULL) < 0) ++ return 1; ++ ++ if (FD_ISSET(fd, &rfds)) { ++ return 1; ++ ++ } else if (netlink_fd >= 0 && FD_ISSET(netlink_fd, &rfds)) { ++ if (!process_netlink()) ++ continue; ++ ++ /* Just drain the inotify buffer */ ++ } else if (inotify_fd >= 0 && FD_ISSET(inotify_fd, &rfds)) { ++ while (read(inotify_fd, buffer, sizeof (buffer)) > 0); ++ } ++ ++ return 0; ++ } ++} ++#endif /* AGETTY_RELOAD */ ++ ++#ifdef ISSUEDIR_SUPPORT ++static int issuedir_filter(const struct dirent *d) ++{ ++ size_t namesz; ++ ++#ifdef _DIRENT_HAVE_D_TYPE ++ if (d->d_type != DT_UNKNOWN && d->d_type != DT_REG && ++ d->d_type != DT_LNK) ++ return 0; ++#endif ++ if (*d->d_name == '.') ++ return 0; ++ ++ namesz = strlen(d->d_name); ++ if (!namesz || namesz < ISSUEDIR_EXTSIZ + 1 || ++ strcmp(d->d_name + (namesz - ISSUEDIR_EXTSIZ), ISSUEDIR_EXT)) ++ return 0; ++ ++ /* Accept this */ ++ return 1; ++} ++ ++static FILE *issuedir_next_file(int dd, struct dirent **namelist, int nfiles, int *n) ++{ ++ while (*n < nfiles) { ++ struct dirent *d = namelist[*n]; ++ struct stat st; ++ FILE *f; ++ ++ (*n)++; ++ ++ if (fstatat(dd, d->d_name, &st, 0) || ++ !S_ISREG(st.st_mode)) ++ continue; ++ ++ f = fopen_at(dd, d->d_name, O_RDONLY|O_CLOEXEC, "r" UL_CLOEXECSTR); ++ if (f) ++ return f; ++ } ++ return NULL; ++} ++ ++#endif /* ISSUEDIR_SUPPORT */ ++ ++#ifndef ISSUE_SUPPORT ++static void print_issue_file(struct options *op, struct termios *tp __attribute__((__unused__))) ++{ ++ if ((op->flags & F_NONL) == 0) { ++ /* Issue not in use, start with a new line. */ ++ write_all(STDOUT_FILENO, "\r\n", 2); ++ } ++} ++#else /* ISSUE_SUPPORT */ ++ ++static void print_issue_file(struct options *op, struct termios *tp) ++{ ++ const char *filename, *dirname = NULL; ++ FILE *f = NULL; ++#ifdef ISSUEDIR_SUPPORT ++ int dd = -1, nfiles = 0, i; ++ struct dirent **namelist = NULL; ++#endif + if ((op->flags & F_NONL) == 0) { + /* Issue not in use, start with a new line. */ + write_all(STDOUT_FILENO, "\r\n", 2); + } + +-#ifdef ISSUE +- if ((op->flags & F_ISSUE) && (fd = fopen(op->issue, "r"))) { ++ if (!(op->flags & F_ISSUE)) ++ return; ++ ++ /* ++ * The custom issue file or directory specified by: agetty -f . ++ * Note that nothing is printed if the file/dir does not exist. ++ */ ++ filename = op->issue; ++ if (filename) { ++ struct stat st; ++ ++ if (stat(filename, &st) < 0) ++ return; ++ if (S_ISDIR(st.st_mode)) { ++ dirname = filename; ++ filename = NULL; ++ } ++ } else { ++ /* The default /etc/issue and optional /etc/issue.d directory ++ * as extension to the file. The /etc/issue.d directory is ++ * ignored if there is no /etc/issue file. The file may be ++ * empty or symlink. ++ */ ++ if (access(_PATH_ISSUE, F_OK|R_OK) != 0) ++ return; ++ filename = _PATH_ISSUE; ++ dirname = _PATH_ISSUEDIR; ++ } ++ ++#ifdef ISSUEDIR_SUPPORT ++ if (dirname) { ++ dd = open(dirname, O_RDONLY|O_CLOEXEC|O_DIRECTORY); ++ if (dd >= 0) ++ nfiles = scandirat(dd, ".", &namelist, issuedir_filter, versionsort); ++ if (nfiles <= 0) ++ dirname = NULL; ++ } ++ i = 0; ++#endif ++ if (filename) ++ f = fopen(filename, "r"); ++ ++ if (f || dirname) { + int c, oflag = tp->c_oflag; /* Save current setting. */ + + if ((op->flags & F_VCONSOLE) == 0) { +@@ -1395,12 +1787,23 @@ static void do_prompt(struct options *op, struct termios *tp) + tcsetattr(STDIN_FILENO, TCSADRAIN, tp); + } + +- while ((c = getc(fd)) != EOF) { +- if (c == '\\') +- output_special_char(getc(fd), op, tp, fd); +- else +- putchar(c); +- } ++ do { ++#ifdef ISSUEDIR_SUPPORT ++ if (!f && i < nfiles) ++ f = issuedir_next_file(dd, namelist, nfiles, &i); ++#endif ++ if (!f) ++ break; ++ while ((c = getc(f)) != EOF) { ++ if (c == '\\') ++ output_special_char(getc(f), op, tp, f); ++ else ++ putchar(c); ++ } ++ fclose(f); ++ f = NULL; ++ } while (dirname); ++ + fflush(stdout); + + if ((op->flags & F_VCONSOLE) == 0) { +@@ -1409,11 +1812,36 @@ static void do_prompt(struct options *op, struct termios *tp) + /* Wait till output is gone. */ + tcsetattr(STDIN_FILENO, TCSADRAIN, tp); + } +- fclose(fd); + } +-#endif /* ISSUE */ ++ ++#ifdef ISSUEDIR_SUPPORT ++ for (i = 0; i < nfiles; i++) ++ free(namelist[i]); ++ free(namelist); ++ if (dd >= 0) ++ close(dd); ++#endif ++} ++#endif /* ISSUE_SUPPORT */ ++ ++/* Show login prompt, optionally preceded by /etc/issue contents. */ ++static void do_prompt(struct options *op, struct termios *tp) ++{ ++#ifdef AGETTY_RELOAD ++again: ++#endif ++ print_issue_file(op, tp); ++ + if (op->flags & F_LOGINPAUSE) { + puts(_("[press ENTER to login]")); ++#ifdef AGETTY_RELOAD ++ if (!wait_for_term_input(STDIN_FILENO)) { ++ /* reload issue */ ++ if (op->flags & F_VCONSOLE) ++ termio_clear(STDOUT_FILENO); ++ goto again; ++ } ++#endif + getc(stdin); + } + #ifdef KDGKBLED +@@ -1533,18 +1961,36 @@ static char *get_logname(struct options *op, struct termios *tp, struct chardata + *bp = '\0'; + + while (*logname == '\0') { +- + /* Write issue file and prompt */ + do_prompt(op, tp); + ++#ifdef AGETTY_RELOAD ++ if (!wait_for_term_input(STDIN_FILENO)) { ++ /* refresh prompt -- discard input data, clear terminal ++ * and call do_prompt() again ++ */ ++ if ((op->flags & F_VCONSOLE) == 0) ++ sleep(1); ++ tcflush(STDIN_FILENO, TCIFLUSH); ++ if (op->flags & F_VCONSOLE) ++ termio_clear(STDOUT_FILENO); ++ bp = logname; ++ *bp = '\0'; ++ continue; ++ } ++#endif + cp->eol = '\0'; + + /* Read name, watch for break and end-of-line. */ + while (cp->eol == '\0') { + + char key; ++ ssize_t readres; + +- if (read(STDIN_FILENO, &c, 1) < 1) { ++ debug("read from FD\n"); ++ readres = read(STDIN_FILENO, &c, 1); ++ if (readres < 0) { ++ debug("read failed\n"); + + /* The terminal could be open with O_NONBLOCK when + * -L (force CLOCAL) is specified... */ +@@ -1558,12 +2004,15 @@ static char *get_logname(struct options *op, struct termios *tp, struct chardata + case ESRCH: + case EINVAL: + case ENOENT: +- break; ++ exit_slowly(EXIT_SUCCESS); + default: + log_err(_("%s: read: %m"), op->tty); + } + } + ++ if (readres == 0) ++ c = 0; ++ + /* Do parity bit handling. */ + if (eightbit) + ascval = c; +@@ -1588,8 +2037,10 @@ static char *get_logname(struct options *op, struct termios *tp, struct chardata + switch (key) { + case 0: + *bp = 0; +- if (op->numspeed > 1) ++ if (op->numspeed > 1 && !(op->flags & F_VCONSOLE)) + return NULL; ++ if (readres == 0) ++ exit_slowly(EXIT_SUCCESS); + break; + case CR: + case NL: +@@ -1627,6 +2078,7 @@ static char *get_logname(struct options *op, struct termios *tp, struct chardata + } + } + } ++ + #ifdef HAVE_WIDECHAR + if ((op->flags & (F_EIGHTBITS|F_UTF8)) == (F_EIGHTBITS|F_UTF8)) { + /* Check out UTF-8 multibyte characters */ +@@ -1637,7 +2089,7 @@ static char *get_logname(struct options *op, struct termios *tp, struct chardata + if (len < 0) + log_err(_("%s: invalid character conversion for login name"), op->tty); + +- wcs = (wchar_t *) malloc((len + 1) * sizeof(wchar_t)); ++ wcs = malloc((len + 1) * sizeof(wchar_t)); + if (!wcs) + log_err(_("failed to allocate memory: %m")); + +@@ -1702,12 +2154,12 @@ static void termio_final(struct options *op, struct termios *tp, struct chardata + case 1: + /* odd parity */ + tp->c_cflag |= PARODD; +- /* do not break */ ++ /* fallthrough */ + case 2: + /* even parity */ + tp->c_cflag |= PARENB; + tp->c_iflag |= INPCK | ISTRIP; +- /* do not break */ ++ /* fallthrough */ + case (1 | 2): + /* no parity bit */ + tp->c_cflag &= ~CSIZE; +@@ -1767,11 +2219,17 @@ static speed_t bcode(char *s) + return 0; + } + +-static void __attribute__ ((__noreturn__)) usage(FILE *out) ++static void __attribute__((__noreturn__)) usage(void) + { ++ FILE *out = stdout; ++ + fputs(USAGE_HEADER, out); +- fprintf(out, _(" %1$s [options] line [baud_rate,...] [termtype]\n" +- " %1$s [options] baud_rate,... line [termtype]\n"), program_invocation_short_name); ++ fprintf(out, _(" %1$s [options] [,...] []\n" ++ " %1$s [options] ,... []\n"), program_invocation_short_name); ++ ++ fputs(USAGE_SEPARATOR, out); ++ fputs(_("Open a terminal and set its mode.\n"), out); ++ + fputs(USAGE_OPTIONS, out); + fputs(_(" -8, --8bits assume 8-bit tty\n"), out); + fputs(_(" -a, --autologin login the specified user automatically\n"), out); +@@ -1782,10 +2240,12 @@ static void __attribute__ ((__noreturn__)) usage(FILE *out) + fputs(_(" -H, --host specify login host\n"), out); + fputs(_(" -i, --noissue do not display issue file\n"), out); + fputs(_(" -I, --init-string set init string\n"), out); ++ fputs(_(" -J --noclear do not clear the screen before prompt\n"), out); + fputs(_(" -l, --login-program specify login program\n"), out); +- fputs(_(" -L, --local-line[=] cotrol local line flag\n"), out); ++ fputs(_(" -L, --local-line[=] control the local line flag\n"), out); + fputs(_(" -m, --extract-baud extract baud rate during connect\n"), out); + fputs(_(" -n, --skip-login do not prompt for login\n"), out); ++ fputs(_(" -N --nonewline do not print a newline before issue\n"), out); + fputs(_(" -o, --login-options options that are passed to login\n"), out); + fputs(_(" -p, --login-pause wait for any key before the login\n"), out); + fputs(_(" -r, --chroot change root to the directory\n"), out); +@@ -1794,18 +2254,29 @@ static void __attribute__ ((__noreturn__)) usage(FILE *out) + fputs(_(" -t, --timeout login process timeout\n"), out); + fputs(_(" -U, --detect-case detect uppercase terminal\n"), out); + fputs(_(" -w, --wait-cr wait carriage-return\n"), out); +- fputs(_(" --noclear do not clear the screen before prompt\n"), out); + fputs(_(" --nohints do not print hints\n"), out); +- fputs(_(" --nonewline do not print a newline before issue\n"), out); + fputs(_(" --nohostname no hostname at all will be shown\n"), out); + fputs(_(" --long-hostname show full qualified hostname\n"), out); + fputs(_(" --erase-chars additional backspace chars\n"), out); + fputs(_(" --kill-chars additional kill chars\n"), out); +- fputs(_(" --help display this help and exit\n"), out); +- fputs(_(" --version output version information and exit\n"), out); +- fprintf(out, USAGE_MAN_TAIL("agetty(8)")); ++ fputs(_(" --chdir chdir before the login\n"), out); ++ fputs(_(" --delay sleep seconds before prompt\n"), out); ++ fputs(_(" --nice run login with this priority\n"), out); ++ fputs(_(" --reload reload prompts on running agetty instances\n"), out); ++ fputs(_(" --list-speeds display supported baud rates\n"), out); ++ printf( " --help %s\n", USAGE_OPTSTR_HELP); ++ printf( " --version %s\n", USAGE_OPTSTR_VERSION); ++ printf(USAGE_MAN_TAIL("agetty(8)")); ++ ++ exit(EXIT_SUCCESS); ++} ++ ++static void list_speeds(void) ++{ ++ const struct Speedtab *sp; + +- exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS); ++ for (sp = speedtab; sp->speed; sp++) ++ printf("%10ld\n", sp->speed); + } + + /* +@@ -1855,6 +2326,13 @@ static void dolog(int priority, const char *fmt, va_list ap) + #endif /* USE_SYSLOG */ + } + ++static void exit_slowly(int code) ++{ ++ /* Be kind to init(8). */ ++ sleep(10); ++ exit(code); ++} ++ + static void log_err(const char *fmt, ...) + { + va_list ap; +@@ -1863,9 +2341,7 @@ static void log_err(const char *fmt, ...) + dolog(LOG_ERR, fmt, ap); + va_end(ap); + +- /* Be kind to init(8). */ +- sleep(10); +- exit(EXIT_FAILURE); ++ exit_slowly(EXIT_FAILURE); + } + + static void log_warn(const char *fmt, ...) +@@ -1877,41 +2353,70 @@ static void log_warn(const char *fmt, ...) + va_end(ap); + } + +-static void output_iface_ip(struct ifaddrs *addrs, const char *iface, sa_family_t family) ++static void print_addr(sa_family_t family, void *addr) ++{ ++ char buff[INET6_ADDRSTRLEN + 1]; ++ ++ inet_ntop(family, addr, buff, sizeof(buff)); ++ printf("%s", buff); ++} ++ ++/* ++ * Prints IP for the specified interface (@iface), if the interface is not ++ * specified then prints the "best" one (UP, RUNNING, non-LOOPBACK). If not ++ * found the "best" interface then prints at least host IP. ++ */ ++static void output_iface_ip(struct ifaddrs *addrs, ++ const char *iface, ++ sa_family_t family) + { +- if (!iface) ++ struct ifaddrs *p; ++ struct addrinfo hints, *info = NULL; ++ char *host = NULL; ++ void *addr = NULL; ++ ++ if (!addrs) + return; + +- if (addrs->ifa_name +- && strcmp(addrs->ifa_name, iface) == 0 +- && addrs->ifa_addr +- && addrs->ifa_addr->sa_family == family) { ++ for (p = addrs; p; p = p->ifa_next) { + +- void *addr = NULL; +- char buff[INET6_ADDRSTRLEN + 1]; ++ if (!p->ifa_name || ++ !p->ifa_addr || ++ p->ifa_addr->sa_family != family) ++ continue; ++ ++ if (iface) { ++ /* Filter out by interface name */ ++ if (strcmp(p->ifa_name, iface) != 0) ++ continue; ++ } else { ++ /* Select the "best" interface */ ++ if ((p->ifa_flags & IFF_LOOPBACK) || ++ !(p->ifa_flags & IFF_UP) || ++ !(p->ifa_flags & IFF_RUNNING)) ++ continue; ++ } + +- switch (addrs->ifa_addr->sa_family) { ++ addr = NULL; ++ switch (p->ifa_addr->sa_family) { + case AF_INET: +- addr = &((struct sockaddr_in *) addrs->ifa_addr)->sin_addr; ++ addr = &((struct sockaddr_in *) p->ifa_addr)->sin_addr; + break; + case AF_INET6: +- addr = &((struct sockaddr_in6 *) addrs->ifa_addr)->sin6_addr; ++ addr = &((struct sockaddr_in6 *) p->ifa_addr)->sin6_addr; + break; + } ++ + if (addr) { +- inet_ntop(addrs->ifa_addr->sa_family, addr, buff, sizeof(buff)); +- printf("%s", buff); ++ print_addr(family, addr); ++ return; + } ++ } + +- } else if (addrs->ifa_next) +- output_iface_ip(addrs->ifa_next, iface, family); +-} +- +-static void output_ip(sa_family_t family) +-{ +- char *host; +- struct addrinfo hints, *info = NULL; ++ if (iface) ++ return; + ++ /* Hmm.. not found the best interface, print host IP at least */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = family; + if (family == AF_INET6) +@@ -1919,9 +2424,6 @@ static void output_ip(sa_family_t family) + + host = xgethostname(); + if (host && getaddrinfo(host, NULL, &hints, &info) == 0 && info) { +- +- void *addr = NULL; +- + switch (info->ai_family) { + case AF_INET: + addr = &((struct sockaddr_in *) info->ai_addr)->sin_addr; +@@ -1930,12 +2432,8 @@ static void output_ip(sa_family_t family) + addr = &((struct sockaddr_in6 *) info->ai_addr)->sin6_addr; + break; + } +- if (addr) { +- char buff[INET6_ADDRSTRLEN + 1]; +- +- inet_ntop(info->ai_family, (void *) addr, buff, sizeof(buff)); +- printf("%s", buff); +- } ++ if (addr) ++ print_addr(family, addr); + + freeaddrinfo(info); + } +@@ -1974,22 +2472,25 @@ static void output_special_char(unsigned char c, struct options *op, + { + struct utsname uts; + +- uname(&uts); +- + switch (c) { + case 's': ++ uname(&uts); + printf("%s", uts.sysname); + break; + case 'n': ++ uname(&uts); + printf("%s", uts.nodename); + break; + case 'r': ++ uname(&uts); + printf("%s", uts.release); + break; + case 'v': ++ uname(&uts); + printf("%s", uts.version); + break; + case 'm': ++ uname(&uts); + printf("%s", uts.machine); + break; + case 'o': +@@ -2066,30 +2567,39 @@ static void output_special_char(unsigned char c, struct options *op, + { + char *var = NULL, varname[64]; + +- if (get_escape_argument(fp, varname, sizeof(varname))) ++ /* \S{varname} */ ++ if (get_escape_argument(fp, varname, sizeof(varname))) { + var = read_os_release(op, varname); +- else if (!(var = read_os_release(op, "PRETTY_NAME"))) +- var = uts.sysname; +- if (var) { +- if (strcmp(varname, "ANSI_COLOR") == 0) +- printf("\033[%sm", var); +- else +- printf("%s", var); +- if (var != uts.sysname) +- free(var); ++ if (var) { ++ if (strcmp(varname, "ANSI_COLOR") == 0) ++ printf("\033[%sm", var); ++ else ++ fputs(var, stdout); ++ } ++ /* \S */ ++ } else if ((var = read_os_release(op, "PRETTY_NAME"))) { ++ fputs(var, stdout); ++ ++ /* \S and PRETTY_NAME not found */ ++ } else { ++ uname(&uts); ++ fputs(uts.sysname, stdout); + } ++ ++ free(var); ++ + break; + } + case 'u': + case 'U': + { + int users = 0; +- struct utmp *ut; +- setutent(); +- while ((ut = getutent())) ++ struct utmpx *ut; ++ setutxent(); ++ while ((ut = getutxent())) + if (ut->ut_type == USER_PROCESS) + users++; +- endutent(); ++ endutxent(); + if (c == 'U') + printf(P_("%d user", "%d users", users), users); + else +@@ -2100,17 +2610,22 @@ static void output_special_char(unsigned char c, struct options *op, + case '6': + { + sa_family_t family = c == '4' ? AF_INET : AF_INET6; ++ struct ifaddrs *addrs = NULL; + char iface[128]; + +- if (get_escape_argument(fp, iface, sizeof(iface))) { /* interface IP */ +- struct ifaddrs *addrs; +- int status = getifaddrs(&addrs); +- if (status != 0) +- break; ++#ifdef AGETTY_RELOAD ++ open_netlink(); ++#endif ++ ++ if (getifaddrs(&addrs)) ++ break; ++ ++ if (get_escape_argument(fp, iface, sizeof(iface))) + output_iface_ip(addrs, iface, family); +- freeifaddrs(addrs); +- } else /* host IP */ +- output_ip(family); ++ else ++ output_iface_ip(addrs, NULL, family); ++ ++ freeifaddrs(addrs); + break; + } + default: +@@ -2162,7 +2677,7 @@ static void init_special_char(char* arg, struct options *op) + } + + /* +- * Appends @str to @dest and if @dest is not empty then use use @sep as a ++ * Appends @str to @dest and if @dest is not empty then use @sep as a + * separator. The maximal final length of the @dest is @len. + * + * Returns the final @dest length or -1 in case of error. +@@ -2217,3 +2732,19 @@ err: + log_err(_("checkname failed: %m")); + } + ++static void reload_agettys(void) ++{ ++#ifdef AGETTY_RELOAD ++ int fd = open(AGETTY_RELOAD_FILENAME, O_CREAT|O_CLOEXEC|O_WRONLY, ++ S_IRUSR|S_IWUSR); ++ if (fd < 0) ++ err(EXIT_FAILURE, _("cannot open %s"), AGETTY_RELOAD_FILENAME); ++ ++ if (futimens(fd, NULL) < 0 || close(fd) < 0) ++ err(EXIT_FAILURE, _("cannot touch file %s"), ++ AGETTY_RELOAD_FILENAME); ++#else ++ /* very unusual */ ++ errx(EXIT_FAILURE, _("--reload is unsupported on your system")); ++#endif ++} +-- +2.20.1 + diff --git a/SPECS/util-linux.spec b/SPECS/util-linux.spec index a7ec932..974ba18 100644 --- a/SPECS/util-linux.spec +++ b/SPECS/util-linux.spec @@ -2,7 +2,7 @@ Summary: A collection of basic system utilities Name: util-linux Version: 2.23.2 -Release: 59%{?dist} +Release: 59%{?dist}.1 License: GPLv2 and GPLv2+ and LGPLv2+ and BSD with advertising and Public Domain Group: System Environment/Base URL: http://en.wikipedia.org/wiki/Util-linux @@ -436,6 +436,11 @@ Patch177: 0177-mount-append-inverting-options-for-mount.-type-on-us.patch # 1616264 - Login to emergency shell failed Patch178: 0178-sulogin-backport-RHEL-8-version.patch +# RHEL7.6.Z +# +# 1664752 - Bull (Atos) server lands up in invalid stty settings on the serial console after boot preventing login +Patch179: 0179-agetty-backport-RHEL-8-version.patch + %description The util-linux package contains a large variety of low-level system utilities that are necessary for a Linux system to function. Among @@ -1204,6 +1209,9 @@ fi %{_libdir}/pkgconfig/uuid.pc %changelog +* Thu Jan 31 2019 Karel Zak 2.23.2-59.el7_6.1 +- fix #1664752 - Bull (Atos) server lands up in invalid stty settings on the serial console after boot preventing login + * Mon Aug 20 2018 Karel Zak 2.23.2-59 - fix #1618711 - exec option in mount unable to override the implicit noexec in user option - fix #1616264 - Login to emergency shell failed