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, T_SCC_BODY_TEXT_LINE 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 8BDF41F513 for ; Wed, 13 Dec 2023 00:50:20 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=80x24.org; s=selector1; t=1702428620; bh=RkMKun0BGHZDQ4q5P5A8fL84T33Ts7ddQQNzW47SNG8=; h=From:To:Subject:Date:In-Reply-To:References:From; b=T3IHutfxG1eRkOYvfwNS2PiELZSMhhceaxE73aD0ka9rHNexBCtE8kW0rneWk49au JCHgVu1r9DQuwskNn9Kqz9KzAEDKhhvRJYz7PBXRRBYoh+qK/opEyrrzr+2qj5cz7h oY6aAyKbxb1I/eiBmHyUhcFyqa+osZFEiP4ZfZbo= From: Eric Wong To: meta@public-inbox.org Subject: [PATCH 04/14] tests: attempt compatibility w/ busybox lsof Date: Wed, 13 Dec 2023 00:50:09 +0000 Message-ID: <20231213005019.26912-5-e@80x24.org> In-Reply-To: <20231213005019.26912-1-e@80x24.org> References: <20231213005019.26912-1-e@80x24.org> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit List-Id: BusyBox lsof(1) ignores the `-p PID' argument and shows the open files for every process it knows about. BusyBox lsof also lacks the `NODE' column of the non-BusyBox implementation, so we'll rely on /proc/PID/fd/ in those cases since the deleted file checks are Linux-only and it's common to have procfs is mounted on /proc on Linux. --- lib/PublicInbox/TestCommon.pm | 23 ++++++++++++++++++++++- t/ds-leak.t | 5 +---- t/httpd-corner.t | 31 +++++++++++-------------------- t/nntpd.t | 16 +++++++--------- t/v2reindex.t | 5 ++--- 5 files changed, 43 insertions(+), 37 deletions(-) diff --git a/lib/PublicInbox/TestCommon.pm b/lib/PublicInbox/TestCommon.pm index 27a758e4..9c413f43 100644 --- a/lib/PublicInbox/TestCommon.pm +++ b/lib/PublicInbox/TestCommon.pm @@ -29,7 +29,7 @@ BEGIN { tcp_host_port test_lei lei lei_ok $lei_out $lei_err $lei_opt test_httpd xbail require_cmd is_xdeeply tail_f ignore_inline_c_missing no_pollerfd no_coredump cfg_new - strace strace_inject); + strace strace_inject lsof_pid); require Test::More; my @methods = grep(!/\W/, @Test::More::EXPORT); eval(join('', map { "*$_=\\&Test::More::$_;" } @methods)); @@ -951,6 +951,26 @@ sub test_httpd ($$;$$) { } }; +# TODO: support fstat(1) on OpenBSD, lsof already works on FreeBSD + Linux +# don't use this for deleted file checks, we only check that on Linux atm +# and we can readlink /proc/PID/fd/* directly +sub lsof_pid ($;$) { + my ($pid, $rdr) = @_; + state $lsof = require_cmd('lsof', 1); + $lsof or skip 'lsof missing/broken', 1; + my @out = xqx([$lsof, '-p', $pid], undef, $rdr); + if ($?) { + undef $lsof; + skip "lsof -p PID broken \$?=$?", 1; + } + my @cols = split ' ', $out[0]; + if (($cols[7] // '') eq 'NODE') { # normal lsof + @out; + } else { # busybox lsof ignores -p, so we DIY it + grep /\b$pid\b/, @out; + } +} + sub no_pollerfd ($) { my ($pid) = @_; my ($re, @cmd); @@ -966,6 +986,7 @@ sub no_pollerfd ($) { my @of = xqx(\@cmd, {}, {2 => \(my $e)}); my $err = $?; skip "$bin broken? (\$?=$err) ($e)", 1 if $err; + @of = grep /\b$pid\b/, @of; # busybox lsof ignores -p is(grep(/$re/, @of), 0, "no $re FDs") or diag explain(\@of); } } diff --git a/t/ds-leak.t b/t/ds-leak.t index 179997eb..f39985e0 100644 --- a/t/ds-leak.t +++ b/t/ds-leak.t @@ -29,11 +29,8 @@ if ('close-on-exec for epoll and kqueue') { is($l, undef, 'cloexec works and sleep(1) is running'); SKIP: { - my $lsof = require_cmd('lsof', 1) or skip 'lsof missing', 1; my $rdr = { 2 => \(my $null) }; - my @of = grep(/$evfd_re/, xqx([$lsof, '-p', $pid], {}, $rdr)); - my $err = $?; - skip "lsof broken ? (\$?=$err)", 1 if $err; + my @of = grep /$evfd_re/, lsof_pid $pid, $rdr; is_deeply(\@of, [], 'no FDs leaked to subprocess'); }; if (defined $pid) { diff --git a/t/httpd-corner.t b/t/httpd-corner.t index da1c24b9..35c88600 100644 --- a/t/httpd-corner.t +++ b/t/httpd-corner.t @@ -639,30 +639,21 @@ SKIP: { }; SKIP: { - skip 'only testing lsof(8) output on Linux', 1 if $^O ne 'linux'; - my $lsof = require_cmd('lsof', 1) or skip 'no lsof in PATH', 1; - my $null_in = ''; - my $rdr = { 2 => \(my $null_err), 0 => \$null_in }; - my @lsof = xqx([$lsof, '-p', $td->{pid}], undef, $rdr); - my $d = [ grep(/\(deleted\)/, @lsof) ]; - is_deeply($d, [], 'no lingering deleted inputs') or diag explain($d); + skip 'only testing /proc/PID/fd on Linux', 1 if $^O ne 'linux'; + my $fd_dir = "/proc/$td->{pid}/fd"; + -d $fd_dir or skip '/proc/$PID/fd missing', 1; + my @child = grep defined, map readlink, glob "$fd_dir/*"; + my @d = grep /\(deleted\)/, @child; + is_deeply(\@d, [], 'no lingering deleted inputs') or diag explain(\@d); # filter out pipes inherited from the parent - my @this = xqx([$lsof, '-p', $$], undef, $rdr); - my $bad; - my $extract_inodes = sub { - map {; - my @f = split(' ', $_); - my $inode = $f[-2]; - $bad = $_ if $inode !~ /\A[0-9]+\z/; - $inode => 1; - } grep (/\bpipe\b/, @_); - }; - my %child = $extract_inodes->(@lsof); + my @this = grep defined, map readlink, glob "/proc/$$/fd/*"; + my $extract_inodes = sub { map { $_ => 1 } grep /\bpipe\b/, @_ }; + my %child = $extract_inodes->(@child); my %parent = $extract_inodes->(@this); - skip("inode not in expected format: $bad", 1) if defined($bad); delete @child{(keys %parent)}; - is_deeply([], [keys %child], 'no extra pipes with -W0'); + is_deeply([], [keys %child], 'no extra pipes with -W0') or + diag explain([child => \%child, parent => \%parent]); }; # ensure compatibility with other PSGI servers diff --git a/t/nntpd.t b/t/nntpd.t index ffe0fd8c..0f3ef596 100644 --- a/t/nntpd.t +++ b/t/nntpd.t @@ -14,7 +14,6 @@ use PublicInbox::DS; my $version = $ENV{PI_TEST_VERSION} || 1; require_git('2.6') if $version == 2; use_ok 'PublicInbox::Msgmap'; -my $lsof = require_cmd('lsof', 1); my $fast_idle = eval { require Linux::Inotify2; 1 } // eval { require IO::KQueue; 1 }; @@ -332,9 +331,7 @@ Date: Fri, 02 Oct 1993 00:00:00 +0000 ($ENV{TEST_RUN_MODE} // 2)) { skip 'Xapian.pm pre-loaded (by xt/check-run.t?)', 1; } - $lsof or skip 'lsof missing', 1; - my @of = xqx([$lsof, '-p', $td->{pid}], undef, $noerr); - skip('lsof broken', 1) if (!scalar(@of) || $?); + my @of = lsof_pid $td->{pid}, $noerr; my @xap = grep m!\bXapian\b!, @of; is_deeply(\@xap, [], 'Xapian not loaded in nntpd') or diag explain(\@of); @@ -364,12 +361,13 @@ Date: Fri, 02 Oct 1993 00:00:00 +0000 tick($fast_idle ? 0.1 : 2.1); $art = $n->article($ex->header('Message-ID')); ok($art, 'new article retrieved after compact'); - $lsof or skip 'lsof missing', 1; - ($^O =~ /\A(?:linux)\z/) or + $^O eq 'linux' or skip "lsof /(deleted)/ check untested on $^O", 1; - my @lsof = xqx([$lsof, '-p', $td->{pid}], undef, $noerr); - my $d = [ grep(/\(deleted\)/, grep(!/batch-command\.err/, @lsof)) ]; - is_deeply($d, [], 'no deleted files') or diag explain($d); + my $fd = "/proc/$td->{pid}/fd"; + -d $fd or skip '/proc/PID/fd missing', 1; + my @of = map readlink, glob "$fd/*"; + my @d = grep /\(deleted\)/, grep !/batch-command\.err/, @of; + is_deeply(\@d, [], 'no deleted files') or diag explain(\@d); }; SKIP: { test_watch($tmpdir, $host_port, $group) }; { diff --git a/t/v2reindex.t b/t/v2reindex.t index 406c0517..8c49e154 100644 --- a/t/v2reindex.t +++ b/t/v2reindex.t @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2021 all contributors +# Copyright (C) all contributors # License: AGPL-3.0+ use strict; use v5.10.1; use PublicInbox::TestCommon; use PublicInbox::Eml; @@ -549,9 +549,8 @@ is($err, '', 'no errors from --xapian-only'); undef $for_destroy; SKIP: { skip 'only testing lsof(8) output on Linux', 1 if $^O ne 'linux'; - my $lsof = require_cmd('lsof', 1) or skip 'no lsof in PATH', 1; my $rdr = { 2 => \(my $null_err) }; - my @d = grep(m!/xap[0-9]+/!, xqx([$lsof, '-p', $$], undef, $rdr)); + my @d = grep m!/xap[0-9]+/!, lsof_pid $$, $rdr; is_deeply(\@d, [], 'no deleted index files') or diag explain(\@d); } done_testing();