user/dev discussion of public-inbox itself
 help / color / mirror / code / Atom feed
From: Eric Wong <e@80x24.org>
To: meta@public-inbox.org
Subject: [PATCH 20/22] lei: run pager in client script
Date: Sun, 10 Jan 2021 12:15:17 +0000	[thread overview]
Message-ID: <20210110121519.17044-21-e@80x24.org> (raw)
In-Reply-To: <20210110121519.17044-1-e@80x24.org>

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 $@;

  parent reply	other threads:[~2021-01-10 12:15 UTC|newest]

Thread overview: 23+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-01-10 12:14 [PATCH 00/22] lei query overview views Eric Wong
2021-01-10 12:14 ` [PATCH 01/22] lei query + pagination sorta working Eric Wong
2021-01-10 12:14 ` [PATCH 02/22] lei q: deduplicate smsg Eric Wong
2021-01-10 12:15 ` [PATCH 03/22] ds: block signals when reaping Eric Wong
2021-01-10 12:15 ` [PATCH 04/22] ipc: add support for asynchronous callbacks Eric Wong
2021-01-10 12:15 ` [PATCH 05/22] cmd_ipc: send FDs with buffer payload Eric Wong
2021-01-10 12:15 ` [PATCH 06/22] ipc: avoid excessive evals Eric Wong
2021-01-10 12:15 ` [PATCH 07/22] ipc: work queue support via SOCK_SEQPACKET Eric Wong
2021-01-10 12:15 ` [PATCH 08/22] ipc: eliminate ipc_worker_stop method Eric Wong
2021-01-10 12:15 ` [PATCH 09/22] ipc: wq: support dynamic worker count change Eric Wong
2021-01-10 12:15 ` [PATCH 10/22] ipc: drop -ipc_parent_pid field Eric Wong
2021-01-10 12:15 ` [PATCH 11/22] ipc: DESTROY and wq_workers methods Eric Wong
2021-01-10 12:15 ` [PATCH 12/22] lei: rename $w to $wpager for warning message Eric Wong
2021-01-10 12:15 ` [PATCH 13/22] lei: fix oneshot TTY detection by passing STD*{GLOB} Eric Wong
2021-01-10 12:15 ` [PATCH 14/22] lei: query: ensure pager exit is instantaneous Eric Wong
2021-01-10 12:15 ` [PATCH 15/22] ipc: start supporting sending/receiving more than 3 FDs Eric Wong
2021-01-10 12:15 ` [PATCH 16/22] ipc: fix IO::FDPass use with a worker limit of 1 Eric Wong
2021-01-10 12:15 ` [PATCH 17/22] ipc: drop unused fields, default sighandlers for wq Eric Wong
2021-01-10 12:15 ` [PATCH 18/22] lei: get rid of client {pid} field Eric Wong
2021-01-10 12:15 ` [PATCH 19/22] lei: fork + FD cleanup Eric Wong
2021-01-10 12:15 ` Eric Wong [this message]
2021-01-10 12:15 ` [PATCH 21/22] lei_xsearch: transfer 4 FDs internally, drop IO::FDPass Eric Wong
2021-01-10 12:15 ` [PATCH 22/22] lei: query: restore JSON output overview Eric Wong

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: http://public-inbox.org/README

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20210110121519.17044-21-e@80x24.org \
    --to=e@80x24.org \
    --cc=meta@public-inbox.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://80x24.org/public-inbox.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).