From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on dcvr.yhbt.net X-Spam-Level: X-Spam-Status: No, score=-4.0 required=3.0 tests=ALL_TRUSTED,AWL,BAYES_00 shortcircuit=no autolearn=ham autolearn_force=no version=3.4.2 Received: from localhost (dcvr.yhbt.net [127.0.0.1]) by dcvr.yhbt.net (Postfix) with ESMTP id 177691FFA8 for ; Sun, 10 Jan 2021 12:15:21 +0000 (UTC) From: Eric Wong To: meta@public-inbox.org Subject: [PATCH 20/22] lei: run pager in client script Date: Sun, 10 Jan 2021 12:15:17 +0000 Message-Id: <20210110121519.17044-21-e@80x24.org> In-Reply-To: <20210110121519.17044-1-e@80x24.org> References: <20210110121519.17044-1-e@80x24.org> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit List-Id: While most single keystrokes work fine when the pager is launched from the background daemon, Ctrl-C and WINCH can cause strangeness when connected to the wrong terminal. --- lib/PublicInbox/LEI.pm | 26 +++++++++++++++++++------- lib/PublicInbox/LeiQuery.pm | 5 +++-- script/lei | 28 +++++++++++++++++++++++++--- 3 files changed, 47 insertions(+), 12 deletions(-) diff --git a/lib/PublicInbox/LEI.pm b/lib/PublicInbox/LEI.pm index 1ef0cbec..d19fb311 100644 --- a/lib/PublicInbox/LEI.pm +++ b/lib/PublicInbox/LEI.pm @@ -26,7 +26,7 @@ use Text::Wrap qw(wrap); use File::Path qw(mkpath); use File::Spec; our $quit = \&CORE::exit; -my $recv_cmd; +my ($recv_cmd, $send_cmd); my $GLP = Getopt::Long::Parser->new; $GLP->configure(qw(gnu_getopt no_ignore_case auto_abbrev)); my $GLP_PASS = Getopt::Long::Parser->new; @@ -244,7 +244,8 @@ sub x_it ($$) { # pronounced "exit" my $sig = ($code & 127); $code >>= 8 unless $sig; if (my $sock = $self->{sock}) { - say $sock "exit=$code"; + my $fds = [ map { fileno($_) } @$self{0..2} ]; + $send_cmd->($sock, $fds, "exit=$code\n", 0); } else { # for oneshot $quit->($code); } @@ -635,15 +636,23 @@ sub start_pager { chomp(my $pager = <$fh> // ''); close($fh) or warn "`git var PAGER' error: \$?=$?"; return if $pager eq 'cat' || $pager eq ''; - $env->{LESS} //= 'FRX'; - $env->{LV} //= '-c'; - $env->{COLUMNS} //= 80; # TODO TIOCGWINSZ - $env->{MORE} //= 'FRX' if $^O eq 'freebsd'; + # TODO TIOCGWINSZ + my %new_env = (LESS => 'FRX', LV => '-c', COLUMNS => 80); + $new_env{MORE} = 'FRX' if $^O eq 'freebsd'; pipe(my ($r, $wpager)) or return warn "pipe: $!"; my $rdr = { 0 => $r, 1 => $self->{1}, 2 => $self->{2} }; + my $pid; + if (my $sock = $self->{sock}) { # lei(1) process runs it + delete @new_env{keys %$env}; # only set iff unset + my $buf = "exec 1\0".$pager; + while (my ($k, $v) = each %new_env) { $buf .= "\0$k=$v" }; + my $fds = [ map { fileno($_) } @$rdr{0..2} ]; + $send_cmd->($sock, $fds, $buf .= "\n", 0); + } else { + $pid = spawn([$pager], $env, $rdr); + } $self->{1} = $wpager; $self->{2} = $wpager if -t $self->{2}; - my $pid = spawn([$pager], $env, $rdr); $env->{GIT_PAGER_IN_USE} = 'true'; # we may spawn git [ $pid, @$rdr{1, 2} ]; } @@ -731,10 +740,13 @@ sub lazy_start { local $oldset = PublicInbox::DS::block_signals(); if ($nfd == 1) { require PublicInbox::CmdIPC1; + $send_cmd = PublicInbox::CmdIPC1->can('send_cmd1'); $recv_cmd = PublicInbox::CmdIPC1->can('recv_cmd1'); } elsif ($nfd == 4) { + $send_cmd = PublicInbox::Spawn->can('send_cmd4'); $recv_cmd = PublicInbox::Spawn->can('recv_cmd4') // do { require PublicInbox::CmdIPC4; + $send_cmd = PublicInbox::CmdIPC4->can('send_cmd4'); PublicInbox::CmdIPC4->can('recv_cmd4'); }; } diff --git a/lib/PublicInbox/LeiQuery.pm b/lib/PublicInbox/LeiQuery.pm index 9a383cef..6e778785 100644 --- a/lib/PublicInbox/LeiQuery.pm +++ b/lib/PublicInbox/LeiQuery.pm @@ -125,9 +125,10 @@ sub lei_q { # my $wcb = PublicInbox::LeiToMail->write_cb($out, $self); $self->{mset_opt} = \%mset_opt; $lxs->do_query($self, \@srcs); - if ($pid_old12) { + if ($pid_old12) { # [ pid, stdout, stderr ] + my $pid = $pid_old12->[0]; $self->{$_} = $pid_old12->[$_] for (1, 2); - dwaitpid($pid_old12->[0], undef, $self->{sock}); + dwaitpid($pid, undef, $self->{sock}) if $pid; } } diff --git a/script/lei b/script/lei index bea06b2c..aac8fa94 100755 --- a/script/lei +++ b/script/lei @@ -6,16 +6,33 @@ use v5.10.1; use Socket qw(AF_UNIX SOCK_STREAM pack_sockaddr_un); use PublicInbox::CmdIPC4; my $narg = 4; +my $recv_cmd = PublicInbox::CmdIPC4->can('recv_cmd4'); my $send_cmd = PublicInbox::CmdIPC4->can('send_cmd4') // do { require PublicInbox::CmdIPC1; # 2nd choice $narg = 1; + $recv_cmd = PublicInbox::CmdIPC1->can('recv_cmd1'); PublicInbox::CmdIPC1->can('send_cmd1'); } // do { require PublicInbox::Spawn; # takes ~50ms even if built *sigh* $narg = 4; + $recv_cmd = PublicInbox::Spawn->can('recv_cmd4'); PublicInbox::Spawn->can('send_cmd4'); }; +sub exec_cmd { + my ($fds, $argc, @argv) = @_; + my %env = map { split(/=/, $_, 2) } splice(@argv, $argc); + my @m = (*STDIN{IO}, '<&=', *STDOUT{IO}, '>&=', + *STDERR{IO}, '>&='); + for my $fd (@$fds) { + my ($old_io, $mode) = splice(@m, 0, 2); + open($old_io, $mode, $fd) or die "open $mode$fd: $!"; + } + %ENV = (%ENV, %env); + exec(@argv); + die "exec: @argv: $!"; +} + my ($sock, $pwd); if ($send_cmd && eval { my $path = do { @@ -68,9 +85,14 @@ Falling back to (slow) one-shot mode select $sock; $| = 1; # unbuffer selected $sock $send_cmd->($sock, [ 0, 1, 2 ], $buf, 0); - while ($buf = <$sock>) { - $buf =~ /\Aexit=([0-9]+)\n\z/ and exit($1 + 0); - die $buf; + while (my (@fds) = $recv_cmd->($sock, $buf, 4096 * 33)) { + if ($buf =~ /\Aexit=([0-9]+)\n\z/) { + exit($1); + } elsif ($buf =~ /\Aexec (.+)\n\z/) { + exec_cmd(\@fds, split(/\0/, $1)); + } else { + die $buf; + } } } else { # for systems lacking Socket::MsgHdr, IO::FDPass or Inline::C warn $@ if $@;