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,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 2320E1FF9E for ; Thu, 14 Jan 2021 07:06:29 +0000 (UTC) From: Eric Wong To: meta@public-inbox.org Subject: [PATCH 14/14] lei: pass FD to CWD via cmsg, use fchdir on server Date: Wed, 13 Jan 2021 19:06:27 -1200 Message-Id: <20210114070627.18195-15-e@80x24.org> In-Reply-To: <20210114070627.18195-1-e@80x24.org> References: <20210114070627.18195-1-e@80x24.org> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit List-Id: Perl chdir() automatically does fchdir(2) if given a file or directory handle since 5.8.8/5.10.0, so we can safely rely on it given our 5.10.1+ requirement. This means we no longer have to waste several milliseconds loading the Cwd.so and making stat() calls to ensure ENV{PWD} is correct and usable in the server. It also lets us work in directories that are no longer accessible via pathname. --- lib/PublicInbox/LEI.pm | 14 +++++++------- script/lei | 18 +++--------------- t/lei.t | 27 ++------------------------- 3 files changed, 12 insertions(+), 47 deletions(-) diff --git a/lib/PublicInbox/LEI.pm b/lib/PublicInbox/LEI.pm index 9786e7ac..1f4a3082 100644 --- a/lib/PublicInbox/LEI.pm +++ b/lib/PublicInbox/LEI.pm @@ -674,9 +674,9 @@ sub accept_dispatch { # Listener {post_accept} callback select($rvec, undef, undef, 1) or return send($sock, 'timed out waiting to recv FDs', MSG_EOR); my @fds = $recv_cmd->($sock, my $buf, 4096 * 33); # >MAX_ARG_STRLEN - if (scalar(@fds) == 3) { + if (scalar(@fds) == 4) { my $i = 0; - for my $rdr (qw(<&= >&= >&=)) { + for my $rdr (qw(<&= >&= >&= <&=)) { my $fd = shift(@fds); open($self->{$i++}, $rdr, $fd) and next; send($sock, "open($rdr$fd) (FD=$i): $!", MSG_EOR); @@ -692,13 +692,13 @@ sub accept_dispatch { # Listener {post_accept} callback my ($argc, @argv) = split(/\0/, $buf, -1); undef $buf; my %env = map { split(/=/, $_, 2) } splice(@argv, $argc); - if (chdir($env{PWD})) { + if (chdir(delete($self->{3}))) { local %ENV = %env; $self->{env} = \%env; eval { dispatch($self, @argv) }; send($sock, $@, MSG_EOR) if $@; } else { - send($sock, "chdir($env{PWD}): $!", MSG_EOR); # implicit close + send($sock, "fchdir: $!", MSG_EOR); # implicit close } } @@ -746,7 +746,7 @@ our $oldset; sub oldset { $oldset } # lei(1) calls this when it can't connect sub lazy_start { - my ($path, $errno, $nfd) = @_; + my ($path, $errno, $narg) = @_; if ($errno == ECONNREFUSED) { unlink($path) or die "unlink($path): $!"; } elsif ($errno != ENOENT) { @@ -761,7 +761,7 @@ sub lazy_start { my $dev_ino_expect = pack('dd', $st[0], $st[1]); # dev+ino pipe(my ($eof_r, $eof_w)) or die "pipe: $!"; local $oldset = PublicInbox::DS::block_signals(); - if ($nfd == 4) { + if ($narg == 5) { $send_cmd = PublicInbox::Spawn->can('send_cmd4'); $recv_cmd = PublicInbox::Spawn->can('recv_cmd4') // do { require PublicInbox::CmdIPC4; @@ -770,7 +770,7 @@ sub lazy_start { }; } $recv_cmd or die <<""; -(Socket::MsgHdr || Inline::C) missing/unconfigured (nfd=$nfd); +(Socket::MsgHdr || Inline::C) missing/unconfigured (narg=$narg); require PublicInbox::Listener; require PublicInbox::EOFpipe; diff --git a/script/lei b/script/lei index 9610a876..a4a0217b 100755 --- a/script/lei +++ b/script/lei @@ -6,7 +6,7 @@ use v5.10.1; use Socket qw(AF_UNIX SOCK_SEQPACKET MSG_EOR pack_sockaddr_un); use Errno qw(EINTR ECONNRESET); use PublicInbox::CmdIPC4; -my $narg = 4; +my $narg = 5; my ($sock, $pwd); my $recv_cmd = PublicInbox::CmdIPC4->can('recv_cmd4'); my $send_cmd = PublicInbox::CmdIPC4->can('send_cmd4') // do { @@ -74,25 +74,13 @@ connect($path): $! (after attempted daemon start) Falling back to (slow) one-shot mode } - require Cwd; - $pwd = $ENV{PWD} // ''; - my $cwd = Cwd::fastcwd() // die "fastcwd(PWD=$pwd): $!"; - if ($pwd ne $cwd) { # prefer ENV{PWD} if it's a symlink to real cwd - my @st_cwd = stat($cwd) or die "stat(cwd=$cwd): $!"; - my @st_pwd = stat($pwd); # PWD invalid, use cwd - # make sure st_dev/st_ino match for {PWD} to be valid - $pwd = $cwd if (!@st_pwd || $st_pwd[1] != $st_cwd[1] || - $st_pwd[0] != $st_cwd[0]); - } else { - $pwd = $cwd; - } 1; }) { # (Socket::MsgHdr|Inline::C), $sock, $pwd are all available: - $ENV{PWD} = $pwd; + open my $dh, '<', '.' or die "open(.) $!"; my $buf = join("\0", scalar(@ARGV), @ARGV); while (my ($k, $v) = each %ENV) { $buf .= "\0$k=$v" } $buf .= "\0\0"; - $send_cmd->($sock, [ 0, 1, 2 ], $buf, MSG_EOR); + $send_cmd->($sock, [ 0, 1, 2, fileno($dh) ], $buf, MSG_EOR); $SIG{TERM} = $SIG{INT} = $SIG{QUIT} = sub { my ($sig) = @_; # 'TERM', not an integer :< $SIG{$sig} = 'DEFAULT'; diff --git a/t/lei.t b/t/lei.t index 240735bf..2349dca4 100644 --- a/t/lei.t +++ b/t/lei.t @@ -208,9 +208,9 @@ if ($ENV{TEST_LEI_ONESHOT}) { SKIP: { # real socket require_mods(qw(Cwd), my $nr = 105); - my $nfd = eval { require Socket::MsgHdr; 4 } // do { + my $nfd = eval { require Socket::MsgHdr; 5 } // do { require PublicInbox::Spawn; - PublicInbox::Spawn->can('send_cmd4') ? 4 : undef; + PublicInbox::Spawn->can('send_cmd4') ? 5 : undef; } // skip 'Socket::MsgHdr or Inline::C missing or unconfigured', $nr; @@ -260,29 +260,6 @@ SKIP: { # real socket like($out, qr/^usage: /, 'help output works'); chmod 0700, $sock or BAIL_OUT "chmod 0700: $!"; } - if ('oneshot on cwd gone') { - my $cwd = Cwd::fastcwd() or BAIL_OUT "fastcwd: $!"; - my $d = "$home/to-be-removed"; - my $lei_path = 'lei'; - # we chdir, so we need an abs_path fur run_script - if (($ENV{TEST_RUN_MODE}//2) != 2) { - $lei_path = PublicInbox::TestCommon::key2script('lei'); - $lei_path = Cwd::abs_path($lei_path); - } - mkdir $d or BAIL_OUT "mkdir($d) $!"; - chdir $d or BAIL_OUT "chdir($d) $!"; - if (rmdir($d)) { - $out = $err = ''; - ok(run_script([$lei_path, 'help'], undef, $opt), - 'cwd fail, one-shot fallback works'); - } else { - $err = "rmdir=$!"; - } - chdir $cwd or BAIL_OUT "chdir($cwd) $!"; - like($err, qr/cwd\(/, 'cwd error noted'); - like($out, qr/^usage: /, 'help output still works'); - } - unlink $sock or BAIL_OUT "unlink($sock) $!"; for (0..100) { kill('CHLD', $new_pid) or last;