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