summaryrefslogtreecommitdiffstats
path: root/term-utils/agetty.c
diff options
context:
space:
mode:
authorKarel Zak2017-06-23 14:26:47 +0200
committerKarel Zak2017-06-23 14:26:47 +0200
commit2a14beb4e9c6cdf4466993741d86e45dd57ddef3 (patch)
tree6462e9b6bd94fdf2b672eae538a39ae3bc5dae24 /term-utils/agetty.c
parentMerge branch 'master' of https://github.com/pali/util-linux (diff)
downloadkernel-qcow2-util-linux-2a14beb4e9c6cdf4466993741d86e45dd57ddef3.tar.gz
kernel-qcow2-util-linux-2a14beb4e9c6cdf4466993741d86e45dd57ddef3.tar.xz
kernel-qcow2-util-linux-2a14beb4e9c6cdf4466993741d86e45dd57ddef3.zip
agetty: fix login name DEL/CTRL^U issue
agetty refresh prompt (/etc/issue file etc.) when requested by inotify or netlink. For this purpose we monitor some file descriptors by select(). The terminal input file descriptor is switched to non-canonical mode before select(). The goal is to be informed about user activity before new-line. The FD is immediately switched back to canonical mode when activity is detected. The side effect is that all not-read-yet chars in the input buffer are lost ... so we need to call read() before switch to canonical mode to save the chars. The original implementation has been based on TIOCSTI ioctl. It returns already read chars back to the terminal input buffer to make them useful for canonical mode. The problem was race (agetty writes to input buffer in the same time as user) and result was reordered chars in login name... so useless. This issue has been later fixed by extra buffer (commit 790119b8850ae13bb4254c5096a54b2aeb355b20) for already read data. And TIOCSTI ioctl has been removed. Unfortunately this solution is also wrong, because the buffer is maintained only by agetty and inaccessible for terminal when user edit (by DEL/CTRL^U) login name in canonical mode. The solution is simple -- just don't try to be smart and keep terminal in canonical mode all time (so terminal controls DEL, CTRL^U, etc) and flush input buffer (=discard unread data) and ask user for login name again after prompt reload. The agetty reload is very rarely situation and for user it's pretty obvious that he has to type login name again (as all terminal has been clear+redraw). Addresses: https://github.com/karelzak/util-linux/issues/454 Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1464148 Signed-off-by: Karel Zak <kzak@redhat.com>
Diffstat (limited to 'term-utils/agetty.c')
-rw-r--r--term-utils/agetty.c70
1 files changed, 15 insertions, 55 deletions
diff --git a/term-utils/agetty.c b/term-utils/agetty.c
index 938fa2f97..5b910011c 100644
--- a/term-utils/agetty.c
+++ b/term-utils/agetty.c
@@ -1579,26 +1579,10 @@ static int process_netlink(void)
return changed;
}
-static int wait_for_term_input(int fd, char *prebuf, size_t prebufsz, size_t *readsz)
+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;
-
- /* 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;
if (inotify_fd == AGETTY_RELOAD_FDNONE) {
/* make sure the reload trigger file exists */
@@ -1639,15 +1623,6 @@ static int wait_for_term_input(int fd, char *prebuf, size_t prebufsz, size_t *re
return 1;
if (FD_ISSET(fd, &rfds)) {
- count = read(fd, buffer, sizeof (buffer));
-
- tcsetattr(fd, TCSANOW, &orig);
-
- if (count > 0 && prebuf && prebufsz) {
- memcpy(prebuf, buffer, min(sizeof(buffer), prebufsz));
- *readsz = count;
- }
-
return 1;
} else if (netlink_fd >= 0 && FD_ISSET(netlink_fd, &rfds)) {
@@ -1659,9 +1634,6 @@ static int wait_for_term_input(int fd, char *prebuf, size_t prebufsz, size_t *re
while (read(inotify_fd, buffer, sizeof (buffer)) > 0);
}
- tcsetattr(fd, TCSANOW, &orig);
-
- /* Need to reprompt */
return 0;
}
}
@@ -1716,7 +1688,7 @@ again:
if (op->flags & F_LOGINPAUSE) {
puts(_("[press ENTER to login]"));
#ifdef AGETTY_RELOAD
- if (!wait_for_term_input(STDIN_FILENO, NULL, 0, NULL)) {
+ if (!wait_for_term_input(STDIN_FILENO)) {
/* reload issue */
if (op->flags & F_VCONSOLE)
termio_clear(STDOUT_FILENO);
@@ -1843,28 +1815,21 @@ static char *get_logname(struct options *op, struct termios *tp, struct chardata
*bp = '\0';
while (*logname == '\0') {
- char prebuf[LOGIN_NAME_MAX] = { 0 };
- size_t presz = 0, precur = 0;
-
/* Write issue file and prompt */
do_prompt(op, tp);
#ifdef AGETTY_RELOAD
- /* If asked to reprompt *before* terminal input arrives, then do so.
- *
- * Note that wait_for_term_input() calls read() and the result
- * is stored to the 'prebuf'. We need this to avoid data lost
- * by terminal attributes reset (and return chars back to the
- * terminal by TIOCSTI is fragile (chars reorder)).
- *
- * The data from 'prebuf' are not printed to the terminal yet
- * (disabled ECHO in wait_for_term_input()).
- */
- if (!wait_for_term_input(STDIN_FILENO,
- prebuf, sizeof(prebuf), &presz)) {
-
+ if (!wait_for_term_input(STDIN_FILENO)) {
+ /* refresh prompt -- discard input data, clear terminal
+ * and call do_prompt() again
+ */
+ if ((op->flags & F_VCONSOLE) == 0)
+ sleep(1);
+ tcflush(STDIN_FILENO, TCIFLUSH);
if (op->flags & F_VCONSOLE)
termio_clear(STDOUT_FILENO);
+ bp = logname;
+ *bp = '\0';
continue;
}
#endif
@@ -1874,15 +1839,10 @@ static char *get_logname(struct options *op, struct termios *tp, struct chardata
while (cp->eol == '\0') {
char key;
- int force_echo = 0;
-
- /* use already read data from buffer */
- if (presz && precur < presz) {
- c = prebuf[precur++];
- force_echo = 1;
- /* read from terminal */
- } else if (read(STDIN_FILENO, &c, 1) < 1) {
+ debug("read from FD\n");
+ if (read(STDIN_FILENO, &c, 1) < 1) {
+ debug("read failed\n");
/* The terminal could be open with O_NONBLOCK when
* -L (force CLOCAL) is specified... */
@@ -1958,7 +1918,7 @@ static char *get_logname(struct options *op, struct termios *tp, struct chardata
break;
if ((size_t)(bp - logname) >= sizeof(logname) - 1)
log_err(_("%s: input overrun"), op->tty);
- if ((tp->c_lflag & ECHO) == 0 || force_echo)
+ if ((tp->c_lflag & ECHO) == 0)
write_all(1, &c, 1); /* echo the character */
*bp++ = ascval; /* and store it */
break;