From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on dcvr.yhbt.net X-Spam-Level: X-Spam-ASN: X-Spam-Status: No, score=-4.2 required=3.0 tests=ALL_TRUSTED,AWL,BAYES_00, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF shortcircuit=no autolearn=ham autolearn_force=no version=3.4.6 Received: from localhost (dcvr.yhbt.net [127.0.0.1]) by dcvr.yhbt.net (Postfix) with ESMTP id DA7B61F487 for ; Tue, 17 Oct 2023 10:11:07 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=80x24.org; s=selector1; t=1697537467; bh=4lbGqIAxkM1urxDP7/YHIcoAXc4pN58OgEHciUq9tDk=; h=From:To:Subject:Date:In-Reply-To:References:From; b=23lIMKm1ztKehROfKZu9/SeDOP76ljgn/mkb2Q3sYsYP1GeqHGPtN977wWIlrcO6D w73DXkn1O+Z0HVIMGwmGmYq2875lX59mTnoOzbIhJcWlOT9DgWuXjNlb0Wd2GqugKr sn/ZT2/Q4KqW6fM/SVZODMNrD642IqfXyAwDBrpM= From: Eric Wong To: meta@public-inbox.org Subject: [RFC 3/3] input_pipe: handle noncanonical TTY Date: Tue, 17 Oct 2023 10:11:06 +0000 Message-Id: <20231017101106.582556-4-e@80x24.org> In-Reply-To: <20231017101106.582556-1-e@80x24.org> References: <20231017101106.582556-1-e@80x24.org> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit List-Id: lei could get a TTY in noncanonical mode for stdin, so rely on VMIN+VTIME to get the desired non-blocking semantics we'd expect from a pipe or socket. This ought to prevent read(2) (Perl sysread) from returning zero when we really want to hit EAGAIN. --- lib/PublicInbox/InputPipe.pm | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/lib/PublicInbox/InputPipe.pm b/lib/PublicInbox/InputPipe.pm index 39aefab2..8358ddd6 100644 --- a/lib/PublicInbox/InputPipe.pm +++ b/lib/PublicInbox/InputPipe.pm @@ -6,6 +6,31 @@ package PublicInbox::InputPipe; use v5.12; use parent qw(PublicInbox::DS); use PublicInbox::Syscall qw(EPOLLIN); +use POSIX (); +use Carp qw(croak carp); + +# I'm not sure what I'm doing w.r.t terminals. +# FIXME needs non-interactive tests +sub unblock_tty ($) { + my ($self) = @_; + my $fd = fileno($self->{sock}); + my $t = POSIX::Termios->new; + $t->getattr($fd) or croak("tcgetattr($fd): $!"); + return if $t->getlflag & POSIX::ICANON; # line-oriented, good + + # make noncanonical mode TTYs behave like a O_NONBLOCK pipe. + # O_NONBLOCK itself isn't well-defined, here, so rely on VMIN + VTIME + my ($vmin, $vtime) = ($t->getcc(POSIX::VMIN), $t->getcc(POSIX::VTIME)); + return if $vmin == 1 && $vtime == 0; + + $t->setcc(POSIX::VMIN, 1); # 1 byte minimum + $t->setcc(POSIX::VTIME, 0); # no timeout + $t->setattr($fd, POSIX::TCSANOW) or croak("tcsetattr($fd): $!"); + + $t->setcc(POSIX::VMIN, $vmin); + $t->setcc(POSIX::VTIME, $vtime); + $self->{restore_termios} = $t; +} sub consume { my ($in, $cb, @args) = @_; @@ -16,11 +41,17 @@ sub consume { $self->requeue; } elsif (-p $in || -s _) { # O_NONBLOCK for sockets and pipes $in->blocking(0); - } # TODO: tty + } elsif (-t $in) { # isatty(3) can't use `_' stat cache + unblock_tty($self); + } } sub close { my ($self) = @_; + if (my $t = delete($self->{restore_termios})) { + my $fd = fileno($self->{sock} // return); + $t->setattr($fd, POSIX::TCSANOW) or carp("tcsetattr($fd): $!") + } $self->{-need_rq} ? delete($self->{sock}) : $self->SUPER::close }