Re: [PATCH] agetty: Reprompt and reprint /etc/issue if we receive SIGUSR1

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 15.07.2014 14:40, Stef Walter wrote:
> On 15.07.2014 11:19, Karel Zak wrote:
>>  Maybe the best would be to implement all within agetty, I mean add
>>  a special command 
>>
>>    agetty --reload [<tty>]
>>
>>  to reload another already running agetty process(es). So rather than
>>  "killall agetty" you have to use "agetty --reload".
>>  
>>  Then all the implementation will be a private thing and only the "--reload"
>>  command line option will be a public API.
> 
> Okay. I'll work on this and keep you posted.

Attached is the updated patch.

The current patch implements --reload. We use inotify on a path in /run
to signal the process. We don't create the file initially until does a
--reload.

Note that I didn't implement the [<tty>] option. It seems like it would
need inotify track multiple files, and as such be prone to races, when
trying to signal all agetty processes.

What do you think?

Stef

>From 33583bff1b4dfb3dcff582d1dec9f4005e8bdafd Mon Sep 17 00:00:00 2001
From: Stef Walter <stefw@xxxxxxxxxx>
Date: Thu, 3 Jul 2014 17:44:41 +0200
Subject: [PATCH] agetty: Reprompt and reprint /etc/issue when asked

Add an 'agetty --reload' command which asks all running agetty
commands to display their prompts again.

Several of the /etc/issue escape codes such as \4 and \S depend on
variable data which can change after the agetty prompt is displayed.
This can cause stale data to be displayed when a user looks at a VT,
especially in cases of DHCP racing with system start up.

We never want this to occur once the user has started typing a
user name. So we detect when the user starts typing, after which
no further reprompting occurs after that point.

Signed-off-by: Stef Walter <stefw@xxxxxxxxxx>
---
 term-utils/agetty.8 |   5 +++
 term-utils/agetty.c | 126 +++++++++++++++++++++++++++++++++++++++++++++++-----
 2 files changed, 119 insertions(+), 12 deletions(-)

diff --git a/term-utils/agetty.8 b/term-utils/agetty.8
index 27a0d1b..c997129 100644
--- a/term-utils/agetty.8
+++ b/term-utils/agetty.8
@@ -252,6 +252,11 @@ Sleep seconds before open tty.
 \-\-nice \fInumber\fP
 Run login with this priority.
 .TP
+\-\-reload
+Ask all running agetty instances to reload and update their displayed prompts,
+if the user has not yet commenced logging in. After doing so the command will
+exit.
+.TP
 \-\-version
 Display version information and exit.
 .TP
diff --git a/term-utils/agetty.c b/term-utils/agetty.c
index 2b5932d..d92da80 100644
--- a/term-utils/agetty.c
+++ b/term-utils/agetty.c
@@ -18,6 +18,7 @@
 #include <signal.h>
 #include <errno.h>
 #include <sys/ioctl.h>
+#include <sys/inotify.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/wait.h>
@@ -112,6 +113,9 @@
 #define LOGIN		"login: "
 #define LOGIN_ARGV_MAX	16		/* Numbers of args for login */
 
+/* Reload trigger file */
+#define GETTY_RELOAD_TRIGGER "/run/agetty.reload"
+
 /*
  * When multiple baud rates are specified on the command line, the first one
  * we will try is the first one specified.
@@ -281,10 +285,14 @@ static ssize_t append(char *dest, size_t len, const char  *sep, const char *src)
 static void check_username (const char* nm);
 static void login_options_to_argv(char *argv[], int *argc, char *str, char *username);
 static int plymouth_command(const char* arg);
+static void reload_agettys(void);
 
 /* Fake hostname for ut_host specified on command line. */
 static char *fakehost;
 
+/* Indicates whether we've been asked (via a signal) to reprompt */
+static int inotify_fd = -1;
+
 #ifdef DEBUGGING
 # include "closestream.h"
 # ifndef DEBUG_OUTPUT
@@ -334,6 +342,10 @@ int main(int argc, char **argv)
 	debug("\n");
 #endif				/* DEBUGGING */
 
+	inotify_fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
+	if (inotify_fd > 0)
+		inotify_add_watch (inotify_fd, GETTY_RELOAD_TRIGGER, IN_ATTRIB | IN_MODIFY);
+
 	/* Parse command-line arguments. */
 	parse_args(argc, argv, &options);
 
@@ -587,6 +599,7 @@ static void parse_args(int argc, char **argv, struct options *op)
 		HELP_OPTION,
 		ERASE_CHARS_OPTION,
 		KILL_CHARS_OPTION,
+		RELOAD_OPTION,
 	};
 	const struct option longopts[] = {
 		{  "8bits",	     no_argument,	 0,  '8'  },
@@ -618,6 +631,7 @@ static void parse_args(int argc, char **argv, struct options *op)
 		{  "nohints",        no_argument,        0,  NOHINTS_OPTION },
 		{  "nohostname",     no_argument,	 0,  NOHOSTNAME_OPTION },
 		{  "long-hostname",  no_argument,	 0,  LONGHOSTNAME_OPTION },
+		{  "reload",         no_argument,        0,  RELOAD_OPTION },
 		{  "version",	     no_argument,	 0,  VERSION_OPTION  },
 		{  "help",	     no_argument,	 0,  HELP_OPTION     },
 		{  "erase-chars",    required_argument,  0,  ERASE_CHARS_OPTION },
@@ -736,6 +750,9 @@ static void parse_args(int argc, char **argv, struct options *op)
 		case KILL_CHARS_OPTION:
 			op->killchars = optarg;
 			break;
+		case RELOAD_OPTION:
+			reload_agettys();
+			exit(EXIT_SUCCESS);
 		case VERSION_OPTION:
 			printf(_("%s from %s\n"), program_invocation_short_name,
 			       PACKAGE_STRING);
@@ -1114,6 +1131,21 @@ static void open_tty(char *tty, struct termios *tp, struct options *op)
 }
 
 /* Initialize termios settings. */
+static void termio_clear(int fd)
+{
+	/*
+	 * Do not write a full reset (ESC c) because this destroys
+	 * the unicode mode again if the terminal was in unicode
+	 * mode.  Also it clears the CONSOLE_MAGIC features which
+	 * are required for some languages/console-fonts.
+	 * Just put the cursor to the home position (ESC [ H),
+	 * erase everything below the cursor (ESC [ J), and set the
+	 * scrolling region to the full window (ESC [ r)
+	 */
+	write_all(fd, "\033[r\033[H\033[J", 9);
+}
+
+/* Initialize termios settings. */
 static void termio_init(struct options *op, struct termios *tp)
 {
 	speed_t ispeed, ospeed;
@@ -1166,18 +1198,8 @@ static void termio_init(struct options *op, struct termios *tp)
 		if ((tp->c_cflag & (CS8|PARODD|PARENB)) == CS8)
 			op->flags |= F_EIGHTBITS;
 
-		if ((op->flags & F_NOCLEAR) == 0) {
-			/*
-			 * Do not write a full reset (ESC c) because this destroys
-			 * the unicode mode again if the terminal was in unicode
-			 * mode.  Also it clears the CONSOLE_MAGIC features which
-			 * are required for some languages/console-fonts.
-			 * Just put the cursor to the home position (ESC [ H),
-			 * erase everything below the cursor (ESC [ J), and set the
-			 * scrolling region to the full window (ESC [ r)
-			 */
-			write_all(STDOUT_FILENO, "\033[r\033[H\033[J", 9);
-		}
+		if ((op->flags & F_NOCLEAR) == 0)
+			termio_clear(STDOUT_FILENO);
 		return;
 	}
 
@@ -1593,6 +1615,60 @@ static void next_speed(struct options *op, struct termios *tp)
 	tcsetattr(STDIN_FILENO, TCSANOW, tp);
 }
 
+static int wait_for_term_input(int fd)
+{
+	char buffer[sizeof(struct inotify_event) + NAME_MAX + 1];
+	struct termios orig, nonc;
+	fd_set rfds;
+	int count;
+	int i;
+
+	/* Our aim here is to fall through if something fails
+         * and not be stuck waiting. On failure assume we have input */
+
+	if (tcgetattr(fd, &orig) != 0)
+		return 1;
+
+	memcpy(&nonc, &orig, sizeof (nonc));
+	nonc.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHOK | ECHOKE);
+	nonc.c_cc[VMIN] = 1;
+	nonc.c_cc[VTIME] = 0;
+
+	if (tcsetattr(fd, TCSANOW, &nonc) != 0)
+		return 1;
+
+	FD_ZERO(&rfds);
+	FD_SET(fd, &rfds);
+	if (inotify_fd >= 0)
+		FD_SET(inotify_fd, &rfds);
+
+	/* If waiting fails, just fall through, presumably reading input will fail */
+	if (select (MAX(fd, inotify_fd) + 1, &rfds, NULL, NULL, NULL) < 0)
+		return 1;
+
+	if (FD_ISSET(fd, &rfds)) {
+		count = read(fd, buffer, sizeof (buffer));
+
+		tcsetattr(fd, TCSANOW, &orig);
+
+		/* Reinject the bytes we read back into the buffer, usually just one byte */
+		for (i = 0; i < count; i++)
+			ioctl(fd, TIOCSTI, buffer + i);
+
+		/* Have terminal input */
+		return 1;
+
+	} else {
+		tcsetattr(fd, TCSANOW, &orig);
+
+		/* Just drain the inotify buffer */
+		while (read(inotify_fd, buffer, sizeof (buffer)) > 0);
+
+		/* Need to reprompt */
+		return 0;
+	}
+}
+
 /* Get user name, establish parity, speed, erase, kill & eol. */
 static char *get_logname(struct options *op, struct termios *tp, struct chardata *cp)
 {
@@ -1628,6 +1704,13 @@ static char *get_logname(struct options *op, struct termios *tp, struct chardata
 		/* Write issue file and prompt */
 		do_prompt(op, tp);
 
+		/* If asked to reprompt *before* terminal input arrives, then do so */
+		if (!wait_for_term_input(STDIN_FILENO)) {
+			if (op->flags & F_VCONSOLE)
+				termio_clear(STDOUT_FILENO);
+			continue;
+		}
+
 		cp->eol = '\0';
 
 		/* Read name, watch for break and end-of-line. */
@@ -2364,3 +2447,22 @@ static int plymouth_command(const char* arg)
 	}
 	return 1;
 }
+
+static void reload_agettys(void)
+{
+	struct timeval tv[2];
+	int fd;
+
+	if (gettimeofday (tv, NULL) < 0)
+		err (1, "couldn't get current time");
+	memcpy (tv, tv + 1, sizeof (struct timeval));
+
+	fd = open(GETTY_RELOAD_TRIGGER, O_CREAT|O_CLOEXEC|O_WRONLY, 0700);
+	if (fd < 0)
+		err (1, "couldn't open: %s", GETTY_RELOAD_TRIGGER);
+
+	if (futimes (fd, tv) < 0 || close (fd) < 0)
+		err (1, "couldn't touch file: %s", GETTY_RELOAD_TRIGGER);
+
+	close (fd);
+}
-- 
1.9.3


[Index of Archives]     [Netdev]     [Ethernet Bridging]     [Linux Wireless]     [Kernel Newbies]     [Security]     [Linux for Hams]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux Admin]     [Samba]

  Powered by Linux