--- /dev/null
+#!/usr/bin/perl -w
+#
+# usage: osstest-depriv-fd-collector <pid>
+#
+# audits that <pid> has only depriv fds, eg for a depriv qemu
+#
+# output is series of lines
+# <class> pass|fail <fd> <info> repeated for each fd
+# [<pid>] <fd> sockinfo... as from fishdescriptor
+# generic unknown <fd> </proc link target>
+#
+# fishdescriptor on every fd in <pid>
+# check what kind of fd it is
+#
+# classes are
+# generic
+# (handled here)
+# appendonly
+# readonly
+# privcmd
+# gntdev
+# evtchn
+# (passed to fishdescriptor and depriv-fd-checker)
+
+# This is part of "osstest", an automated testing framework for Xen.
+# Copyright (C) 2018 Citrix Inc.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+use strict;
+
+die unless @ARGV==2;
+our ($pid,$checker) = @ARGV;
+die unless $pid =~ m{^\d+$};
+
+our @fdchecks;
+our @fdsockinfos;
+
+open F, "find /proc/$pid/fd/* -printf '%f %l\\n' |" or die $!;
+while (<F>) {
+ chomp;
+ die "$_ ?" unless s/^(\d+) //;
+ my $fd = $1;
+ if (m{^/dev/null$} ||
+ m{^pipe:} ||
+ m{^anon_inode:\[(?:signalfd|eventfd|eventpoll)\]$}) {
+ print "generic pass $fd $_\n" or die $!;
+ } elsif (m{^/var/log/xen/qemu-dm-.*\.log$}) {
+ push @fdchecks, [ $fd, 'appendonly', $_ ];
+ } elsif (m{^/dev/urandom$} ||
+ m{^/root/.*.iso}) {
+ push @fdchecks, [ $fd, 'readonly', $_ ];
+ } elsif (m{^/dev/xen/(privcmd|gntdev|evtchn)$}) {
+ push @fdchecks, [ $fd, $1, $_ ];
+ } elsif (m{^/dev/net/tun$}) {
+ push @fdchecks, [ $fd, 'tun', $_ ];
+ } elsif (m{^socket:}) {
+ push @fdsockinfos, $fd;
+ } else {
+ print "generic unknown $fd $_\n" or die $!;
+ }
+}
+close F or die "$? $!";
+
+my @fish = (qw(fishdescriptor), $pid);
+my @chk = $checker;
+
+foreach my $fd (@fdsockinfos) {
+ push @fish, $fd, qw(sockinfo);
+}
+
+foreach my $fdi (@fdchecks) {
+ my ($fd, $class, $rhs) = @$fdi;
+ my $fish_fd = $fd + 100;
+ push @fish, "$fish_fd=$fd";
+ push @chk, $class, $fish_fd, "$fd=$rhs";
+}
+
+my @cmd = (@fish, qw(exec), @chk);
+
+print STDERR "$0 running @cmd\n";
+
+exec @cmd or die $!;
--- /dev/null
+#!/usr/bin/perl -w
+# This is part of "osstest", an automated testing framework for Xen.
+# Copyright (C) 2018 Citrix Inc.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+use strict qw(vars);
+use DBI;
+BEGIN { unshift @INC, qw(.); }
+use Osstest;
+use Osstest::TestSupport;
+
+use Data::Dumper;
+$Data::Dumper::Useqq = 1;
+
+tsreadconfig();
+
+our $mode = shift @ARGV;
+our $modesubproc = ${*::}{"mode_$mode"};
+die "unknown mode $mode ?" unless $modesubproc;
+
+our ($ho,$gho) = ts_get_host_guest(@ARGV);
+
+
+our $data_re;
+our $fish_output;
+
+sub compile_data_re () {
+ $data_re = join '|', map { chomp; qr{$_}; } <DATA>;
+}
+
+sub fish_guest () {
+ $fish_output = target_cmd_output_root($ho, <<END.<<'END');
+ set -ex
+ domid=\$(xl domid '$gho->{Name}')
+END
+ qpid=$(xenstore-read /local/domain/$domid/image/device-model-pid)
+ uid=$(id -u xen-qemuuser-range-base)
+ uid=$(( $uid + $domid ))
+ test -d /run/user || mkdir -m 2755 /run/user
+ if mkdir -m 2700 /run/user/$uid; then
+ chown $uid:root /run/user/$uid
+ fi
+ osstest-depriv-fd-collector $qpid \
+ /usr/local/lib/xen/bin/depriv-fd-checker
+END
+ stashfilecontents($fish_output,"fish-info-paused.txt");
+}
+
+sub packages () {
+ target_install_packages_norec($ho, qw(python3 gdb libsocket-msghdr-perl));
+ # ^ fishdescriptor needs those
+}
+
+#----- actual auditing core -----
+
+our %classes;
+our $disk_dev;
+
+sub audit_prep () {
+ $disk_dev = $gho->{Diskimg} || $gho->{Lvdev};
+ $disk_dev = target_cmd_output_root($ho, "realpath $disk_dev");
+ logm("real guest disk device: $disk_dev");
+
+ logm("prep complete, starting audit...");
+ foreach my $cl (qw(privcmd gntdev evtchn other xenstore)) {
+ $classes{$cl} = '';
+ substep_start("/$cl");
+ }
+}
+
+sub fd ($$$) {
+ my ($class,$passfail,$info) = @_;
+ die "$class ?" unless defined $classes{$class};
+ $classes{$class} ||= 'pass';
+ $classes{$class} = $passfail unless $passfail eq 'pass';
+ printf "%-10s %-7s %-20s | %s\n", $class, $passfail, $info, $_ or die $!;
+}
+
+sub audit_fish () {
+ foreach $_ (split /\n/, $fish_output) {
+ if (m{^(privcmd|gntdev|evtchn) (pass|fail) }) {
+ fd($1,$2, 'checked xen');
+ } elsif (m{^\w+ checking }) {
+ printf "%-39s | %s\n", '', $_;
+ } elsif (m{^(\w+) (pass|fail) }) {
+ fd('other',$2, "checked $1");
+ } elsif (m{^\[\d+\] \d+ sockinfo\s+AF_UNIX\tb''\t'/var/run/xenstored/socket'}) {
+ fd('xenstore','fail','xenstore socket');
+ } elsif (m{^generic unknown \d+ \Q$disk_dev\E$}) {
+ fd('other','pass','disk device');
+ } elsif (m{$data_re}) {
+ fd('other','pass','pattern (DATA)');
+ } elsif (m{^\w+ \w+ }) {
+ fd('other','fail',"confusing");
+ } else {
+ local $Data::Dumper::Terse = 1;
+ $_ = Dumper($_);
+ chomp;
+ fd('other','fail','unknown');
+ }
+ }
+
+ foreach my $cl (sort keys %classes) {
+ substep_finish("/$cl", $classes{$cl} || 'pass');
+ # (if there are no fds of this class, maybe they were all closed)
+ }
+ logm("audit complete...");
+}
+
+#----- administrivia, main program, etc. -----
+
+sub mode_create () {
+ packages();
+ guest_create_paused($gho);
+ audit_prep();
+ fish_guest();
+ audit_fish();
+ guest_unpause($gho);
+ guest_await($gho, target_var($gho,'boot_timeout'));
+ guest_check_up($gho);
+}
+
+sub mode_ispaused () {
+ packages();
+ audit_prep();
+ fish_guest();
+ audit_fish();
+}
+
+compile_data_re();
+$modesubproc->();
+
+logm("audited operation complete.");
+
+#----- tolerate these -----
+
+__DATA__
+^generic unknown \d+ /var/log/xen/osstest-serial-.*\.log$
+^generic unknown \d+ /root/\d+\..*\.serial\.in$
+^\[\d+\] \d+ sockinfo\s+AF_UNIX\t+'/var/run/xen/qmp-(?:libxl|libxenstat)-\d+'\t
+^\[\d+\] \d+ sockinfo\s+AF_INET\t+\('[0-9:.]+', 59\d\d\)\t
+^tun maybe \d+ vif\d+\.0-emu \d+=