dcavalca / rpms / util-linux

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