Re: [PATCH v3 2/4] terminal: don't assume stdin is /dev/tty
- From
Junio C Hamano <gitster@pobox.com>
- Date
- Mar 15, 2022, 17:42 UTC
- Message-ID
- <xmqqzglrdsd8.fsf@gitster.g>
- In-Reply-To
- <20220315105723.19398-3-phillip.wood123@gmail.com>
Phillip Wood <phillip.wood123@gmail.com> writes:
Show 6 quoted lines
> diff --git a/compat/terminal.c b/compat/terminal.c > index da2f788137..bfbde3c1af 100644 > --- a/compat/terminal.c > +++ b/compat/terminal.c > @@ -23,21 +23,28 @@ static void restore_term_on_signal(int sig) > static int term_fd = -1;
The variable can be -1 to signal "no valid file descriptor".
Show 9 quoted lines
> static struct termios old_term;
>
> +static void close_term_fd(void)
> +{
> + if (term_fd)
> + close(term_fd);
> + term_fd = -1;
> +}
> +And we use that negative value after closing it.
The check before closing it is wrong. It should be
if (0 <= term_fd)
instead. Or we could mimick the beginning of restore_term(), i.e.
if (term_fd < 0) return; close(term_fd); term_fd = -1;
Show 9 quoted lines
> void restore_term(void)
> {
> if (term_fd < 0)
> return;
>
> tcsetattr(term_fd, TCSAFLUSH, &old_term);
> - close(term_fd);
> - term_fd = -1;
> + close_term_fd();Because we come this far only when term_fd is valid, this change is a no-op. If we are adding more calls to close_term_fd(), it may be a good abstraction.
Show 9 quoted lines
> sigchain_pop_common();
> }
>
> int save_term(enum save_term_flags flags)
> {
> if (term_fd < 0)
> - term_fd = open("/dev/tty", O_RDWR);
> + term_fd = (flags & SAVE_TERM_STDIN) ? 0
> + : open("/dev/tty", O_RDWR);We can avoid overly long line by wrapping differently:
term_fd = ((flags & SAVE_TERM_STDIN)
? 0
: open("/dev/tty", O_RDWR));We can turn our head sideways to see the parse tree this way.
Show 7 quoted lines
> @@ -66,8 +73,7 @@ static int disable_bits(enum save_term_flags flags, tcflag_t bits) > > sigchain_pop_common(); > error: > - close(term_fd); > - term_fd = -1; > + close_term_fd();
OK.
Show 9 quoted lines
> @@ -362,7 +368,7 @@ int read_key_without_echo(struct strbuf *buf)
> static int warning_displayed;
> int ch;
>
> - if (warning_displayed || enable_non_canonical(0) < 0) {
> + if (warning_displayed || enable_non_canonical(SAVE_TERM_STDIN) < 0) {
> if (!warning_displayed) {
> warning("reading single keystrokes not supported on "
> "this platform; reading line instead");The validity of this change is harder to see with only what we have in the patch, but the control flow is
enable_non_canonical(bits)
-> disable_bits(bits, ...)
-> save_term(bits)And we are passing SAVE_TERM_STDIN to say "reuse fd #0 and save the terminal settings of it, instead of opening /dev/tty anew".
What happens if FD#0 is *not* connected to a tty, by the way? tcgetattr() in save_term() would fail, without clearing term_fd (i.e. term_fd is still 0 when save_term() returns a failure), and goes to the error code path, where close_term_fd() is called.
Because we have the "if (term_fd)" bug in save_term(), this bug is hidden, but I think save_term() upon seeing a failure from tcgetattr() should clear term_fd to limit the damage, especially when it is trying to futz with caller supplied FD#0, not the tty it opened itself?
Show 13 quoted lines
> diff --git a/compat/terminal.h b/compat/terminal.h
> index aeb24c9478..79ed00cf61 100644
> --- a/compat/terminal.h
> +++ b/compat/terminal.h
> @@ -4,6 +4,8 @@
> enum save_term_flags {
> /* Save input and output settings */
> SAVE_TERM_DUPLEX = 1 << 0,
> + /* Save stdin rather than /dev/tty (fails if stdin is not a terminal) */
> + SAVE_TERM_STDIN = 1 << 1,
> };
>
> /*