]> git.ipfire.org Git - thirdparty/public-inbox.git/commitdiff
tests: attempt compatibility w/ busybox lsof
authorEric Wong <e@80x24.org>
Wed, 13 Dec 2023 00:50:09 +0000 (00:50 +0000)
committerEric Wong <e@80x24.org>
Wed, 13 Dec 2023 09:01:48 +0000 (09:01 +0000)
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
t/ds-leak.t
t/httpd-corner.t
t/nntpd.t
t/v2reindex.t

index 27a758e4c3a310c663fc050ee62b350ae6a8a4ee..9c413f43f5c7b4c41861c0139ed47404ba51c7c5 100644 (file)
@@ -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);
        }
 }
index 179997eba06718913e0f962b18e91c2bae01b21e..f39985e008e5950685149cf72479b5a8e8ed2a59 100644 (file)
@@ -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) {
index da1c24b951c0dde7e6f1fb425095135d1ef4a349..35c88600090bd9b941761b94ef11c81384e3c07c 100644 (file)
@@ -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
index ffe0fd8c1e73639afcd82155bc5191ba44d65f0f..0f3ef596b01f1ccb07b2f74465bde40e46e646b9 100644 (file)
--- 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) };
        {
index 406c051769654148c3fc7c47d94248b2869860f2..8c49e15409b4de0e6604e1c105f05d5d8d07e05e 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (C) 2018-2021 all contributors <meta@public-inbox.org>
+# Copyright (C) all contributors <meta@public-inbox.org>
 # License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
 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();