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