Only add trailing carriage return to messages if output is a raw tty.

If output is being written to a terminal in "raw" mode, we need to
add a carriage return after the newline to avoid "stair-step" output.
However, we should not write the carriage return if the terminal
is in "cooked" mode, output to a pipe, or output redirected to a file.
Bug #1042.
This commit is contained in:
Todd C. Miller
2022-10-20 08:55:26 -06:00
parent d744271a63
commit 803998d2bd
5 changed files with 43 additions and 9 deletions

View File

@@ -349,6 +349,8 @@ sudo_dso_public bool sudo_term_raw_v1(int fd, int isig);
#define sudo_term_raw(_a, _b) sudo_term_raw_v1((_a), (_b)) #define sudo_term_raw(_a, _b) sudo_term_raw_v1((_a), (_b))
sudo_dso_public bool sudo_term_restore_v1(int fd, bool flush); sudo_dso_public bool sudo_term_restore_v1(int fd, bool flush);
#define sudo_term_restore(_a, _b) sudo_term_restore_v1((_a), (_b)) #define sudo_term_restore(_a, _b) sudo_term_restore_v1((_a), (_b))
sudo_dso_public bool sudo_term_is_raw_v1(int fd);
#define sudo_term_is_raw(_a) sudo_term_is_raw_v1((_a))
/* ttyname_dev.c */ /* ttyname_dev.c */
sudo_dso_public char *sudo_ttyname_dev_v1(dev_t tdev, char *name, size_t namelen); sudo_dso_public char *sudo_ttyname_dev_v1(dev_t tdev, char *name, size_t namelen);

View File

@@ -239,7 +239,7 @@ warning(const char *errstr, const char *fmt, va_list ap)
fputs(": ", stderr); fputs(": ", stderr);
fputs(errstr, stderr); fputs(errstr, stderr);
} }
if (isatty(fileno(stderr))) if (sudo_term_is_raw(fileno(stderr)))
putc('\r', stderr); putc('\r', stderr);
putc('\n', stderr); putc('\n', stderr);
} }

View File

@@ -288,3 +288,27 @@ sudo_term_copy_v1(int src, int dst)
debug_return_bool(true); debug_return_bool(true);
} }
/*
* Returns true if fd refers to a tty in raw mode, else false.
*/
bool
sudo_term_is_raw_v1(int fd)
{
struct termios term;
debug_decl(sudo_term_is_raw, SUDO_DEBUG_UTIL);
if (tcgetattr(fd, &term) != 0)
debug_return_bool(false);
if (term.c_cc[VMIN] != 1 || term.c_cc[VTIME] != 0)
debug_return_bool(false);
if (ISSET(term.c_oflag, OPOST))
debug_return_bool(false);
if (ISSET(term.c_oflag, ECHO|ECHONL|ICANON))
debug_return_bool(false);
debug_return_bool(true);
}

View File

@@ -145,6 +145,7 @@ sudo_term_cbreak_v1
sudo_term_copy_v1 sudo_term_copy_v1
sudo_term_eof sudo_term_eof
sudo_term_erase sudo_term_erase
sudo_term_is_raw_v1
sudo_term_kill sudo_term_kill
sudo_term_noecho_v1 sudo_term_noecho_v1
sudo_term_raw_v1 sudo_term_raw_v1

View File

@@ -91,11 +91,16 @@ sudo_conversation(int num_msgs, const struct sudo_conv_message msgs[],
const char *crnl = NULL; const char *crnl = NULL;
bool written = false; bool written = false;
int ttyfd = -1; int ttyfd = -1;
bool raw_tty = false;
if (ISSET(msg->msg_type, SUDO_CONV_PREFER_TTY) && if (ISSET(msg->msg_type, SUDO_CONV_PREFER_TTY) &&
!ISSET(tgetpass_flags, TGP_STDIN)) !ISSET(tgetpass_flags, TGP_STDIN)) {
ttyfd = open(_PATH_TTY, O_WRONLY); ttyfd = open(_PATH_TTY, O_WRONLY);
if (len != 0 && (ttyfd != -1 || isatty(fileno(fp)))) { raw_tty = sudo_term_is_raw(ttyfd);
} else {
raw_tty = sudo_term_is_raw(fileno(fp));
}
if (len != 0 && raw_tty) {
/* Convert nl -> cr nl in case tty is in raw mode. */ /* Convert nl -> cr nl in case tty is in raw mode. */
if (msg->msg[len - 1] == '\n') { if (msg->msg[len - 1] == '\n') {
if (len == 1 || msg->msg[len - 2] != '\r') { if (len == 1 || msg->msg[len - 2] != '\r') {
@@ -177,12 +182,14 @@ sudo_conversation_printf(int msg_type, const char *fmt, ...)
case SUDO_CONV_INFO_MSG: case SUDO_CONV_INFO_MSG:
/* Convert nl -> cr nl in case tty is in raw mode. */ /* Convert nl -> cr nl in case tty is in raw mode. */
len = strlen(fmt); len = strlen(fmt);
if (len < ssizeof(fmt2) && len > 0 && fmt[len - 1] == '\n') { if (sudo_term_is_raw(fileno(ttyfp ? ttyfp : fp))) {
if (len == 1 || fmt[len - 2] != '\r') { if (len < ssizeof(fmt2) && len > 0 && fmt[len - 1] == '\n') {
memcpy(fmt2, fmt, len - 1); if (len == 1 || fmt[len - 2] != '\r') {
fmt2[len - 1] = '\0'; memcpy(fmt2, fmt, len - 1);
fmt = fmt2; fmt2[len - 1] = '\0';
crnl = "\r\n"; fmt = fmt2;
crnl = "\r\n";
}
} }
} }
va_start(ap, fmt); va_start(ap, fmt);