File Coverage

blib/lib/Sys/Export/Linux.pm
Criterion Covered Total %
statement 37 98 37.7
branch 3 50 6.0
condition 3 29 10.3
subroutine 8 10 80.0
pod 2 2 100.0
total 53 189 28.0


line stmt bran cond sub pod time code
1             package Sys::Export::Linux;
2              
3             # ABSTRACT: Export subsets of a Linux system
4             our $VERSION = '0.005'; # VERSION
5              
6              
7 2     2   423179 use v5.26;
  2         8  
8 2     2   9 use warnings;
  2         3  
  2         106  
9 2     2   8 use experimental qw( signatures );
  2         2  
  2         10  
10 2     2   192 use parent 'Sys::Export::Unix';
  2         3  
  2         12  
11 2     2   100 use Cwd 'abs_path';
  2         2  
  2         77  
12 2     2   6 use Carp;
  2         3  
  2         2644  
13              
14             sub _build__trace_deps {
15 1     1   2 my $self= shift;
16 1 50       4 if ($self->_can_run_in_src) {
17             # Are we going to attempt chrooting?
18 1 50       3 if ($self->src_abs ne '/') {
19 0   0     0 $self->{cmd_path_chroot} //= do {
20 0         0 chomp(my $chroot= `which chroot`);
21 0 0       0 -x $chroot or croak "chroot command not available or not executable";
22 0 0       0 $self->{_log_trace}->("chroot binary at $chroot") if $self->{_log_trace};
23 0         0 $chroot;
24             };
25             }
26 1   33     16 $self->{cmd_path_strace} //= do {
27 1         5653 chomp(my $strace= `which strace`);
28 1 50       496 -x $strace or croak "strace command not available or not executable";
29 0 0       0 $self->{_log_trace}->("strace binary at $strace") if $self->{_log_trace};
30 0         0 $strace;
31             };
32 0         0 return $self->can('_trace_deps_linux_strace');
33             }
34 0         0 $self->next::method(@_);
35             }
36              
37 0     0   0 sub _trace_deps_linux_strace($self, @argv) {
  0         0  
  0         0  
  0         0  
38             # Are we going to attempt chrooting?
39 0 0       0 unshift @argv, $self->{cmd_path_chroot}, $self->src_abs
40             unless $self->src_abs eq '/';
41             # Tell strace to write to a pipe, while redirecting command output to /dev/null
42 0 0       0 open my $devnull, '+<', '/dev/null' or croak "open(/dev/null): $!";
43 0   0     0 pipe(my $r, my $w) // croak "pipe: $!";
44 0   0     0 pipe(my $err_r, my $err_w) // croak "pipe: $!";
45 0   0     0 my $pid= fork // croak "fork: $!";
46 0 0       0 if (!$pid) {
47 0         0 close $r;
48 0         0 close $err_r;
49 0         0 eval {
50 0 0       0 chdir $self->src_abs or die "chdir: $!";
51 0 0       0 POSIX::dup2(fileno $devnull, 0) or die "dup2(->0): $!";
52 0 0       0 POSIX::dup2(fileno $devnull, 1) or die "dup2(->1): $!";
53 0 0       0 POSIX::dup2(fileno $devnull, 2) or die "dup2(->2): $!";
54 0 0       0 POSIX::dup2(fileno $w, 3) or die "dup2(->3): $!";
55 0         0 $^F= 3;
56 0         0 unshift @argv, $self->{cmd_path_strace}, -o => "/proc/self/fd/3", -e => 'trace=open,openat';
57 0 0       0 exec @argv
58             or die "exec(@argv): $!";
59             };
60 0         0 $err_w->print($@);
61 0         0 $err_w->close;
62 0         0 POSIX::_exit(2); # forcibly exit rather than bubble up the stack
63             }
64             else {
65 0         0 close $w;
66 0         0 close $err_w;
67 0         0 my %deps;
68 0         0 my $err= do { local $/; <$err_r> };
  0         0  
  0         0  
69 0 0       0 if (length $err) {
70 0         0 waitpid($pid, 0);
71 0         0 die $err;
72             }
73 0 0       0 $self->{_log_trace}->("Reading strace output") if $self->{_log_trace};
74 0         0 while (<$r>) {
75 0 0       0 $self->{_log_trace}->($_) if $self->{_log_trace};
76 0 0       0 $deps{$1}= 1 if /^open(?:at)?\(.*?"(.*?)",.*?= [0-9]/;
77             }
78 0 0       0 $self->{_log_trace}->("Done reading strace") if $self->{_log_trace};
79 0         0 waitpid($pid,0);
80 0         0 my $wstat= $?;
81 0 0       0 $self->{_log_trace}->("Command exited with $wstat") if $self->{_log_trace};
82 0 0       0 croak "straced command failed: wstat = $wstat"
83             if $wstat;
84 0         0 return \%deps;
85             }
86             }
87              
88              
89 1     1 1 7 sub add_passwd($self, %options) {
  1         2  
  1         2  
  1         1  
90             # If the dst_userdb hasn't been created, create it by filtering the src_userdb by which
91             # group and user ids have been seen during the export.
92             my $db= $self->dst_userdb // Sys::Export::Unix::UserDB->new(
93 1   0     6 auto_import => ($self->{src_userdb} //= $self->_build_src_userdb),
      33        
94             );
95 1         4 $db->group($_) for keys $self->dst_gid_used->%*;
96 1         5 $db->user($_) for keys $self->dst_uid_used->%*;
97 1         4 $db->save(\my %contents);
98 1   50     4 my $etc_path= $options{etc_path} // 'etc';
99 1         13 $self->add([ dir755 => "$etc_path", { uid => 0, gid => 0 }]);
100 1         7 $self->add([ file644 => "$etc_path/passwd", $contents{passwd}, { uid => 0, gid => 0 }]);
101 1         10 $self->add([ file600 => "$etc_path/shadow", $contents{shadow}, { uid => 0, gid => 0 }]);
102 1         6 $self->add([ file644 => "$etc_path/group", $contents{group}, { uid => 0, gid => 0 }]);
103 1         3 $self;
104             }
105              
106              
107 0     0 1   sub add_localtime($self, $tz_name) {
  0            
  0            
  0            
108 0 0 0       if (exists $self->{dst_path_set}{"usr/share/zoneinfo/$tz_name"}
    0 0        
    0          
109             || ($self->_dst->can('dst_abs') && -f $self->_dst->dst_abs . $tz_name)
110             ) {
111             # zoneinfo is exported, and includes this timezone, so symlink to it
112 0           $self->add([ sym => "etc/localtime" => "../usr/share/zoneinfo/$tz_name" ]);
113             }
114             elsif (defined (my $src_path= $self->_src_abs_path("usr/share/zoneinfo/$tz_name"))) {
115 0           $self->add([ file644 => 'etc/localtime', { data_path => $self->src_abs . $src_path } ]);
116             }
117             elsif (defined (my $path= abs_path("/usr/share/zoneinfo/$tz_name"))) {
118 0           $self->add([ file644 => 'etc/localtime', { data_path => $path } ]);
119             }
120             else {
121 0           croak "Can't find 'usr/share/zoneinfo/$tz_name' in destination, source, or host filesystem";
122             }
123             }
124              
125             # Avoiding dependency on namespace::clean
126             delete @{Sys::Export::Linux::}{qw( croak carp confess abs_path )};
127             1;
128              
129             __END__