File Coverage

blib/lib/Sys/Linux/Namespace.pm
Criterion Covered Total %
statement 49 83 59.0
branch 11 36 30.5
condition 3 27 11.1
subroutine 13 18 72.2
pod 2 5 40.0
total 78 169 46.1


line stmt bran cond sub pod time code
1             package Sys::Linux::Namespace;
2             # ABSTRACT: Sets up linux kernel namespaces
3              
4 1     1   41079 use strict;
  1         2  
  1         25  
5 1     1   5 use warnings;
  1         1  
  1         25  
6              
7 1     1   284 use Sys::Linux::Mount qw/:all/;
  1         4  
  1         154  
8             #use Sys::Linux::Unshare qw/:all/;
9 1     1   393 use Linux::Clone;
  1         251  
  1         25  
10 1     1   377 use POSIX qw/_exit/;
  1         4204  
  1         5  
11 1     1   1502 use Time::HiRes qw/sleep/;
  1         953  
  1         4  
12              
13 1     1   536 use Moo;
  1         10228  
  1         4  
14 1     1   1091 use Carp qw/croak/;
  1         1  
  1         708  
15              
16             has no_proc => (is => 'rw');
17             has term_child => (is => 'rw', default => 1);
18              
19             our $debug = 0;
20             sub debug {
21 1 50   1 0 8 print STDERR @_ if $debug;
22             }
23              
24             our $VERSION = v0.015;
25             my @signames = grep {!/^__/} keys %SIG; # capture before anyone has probably localized it.
26              
27             for my $p (qw/tmp mount pid net ipc user uts sysvsem/) {
28             my $pp = "private_$p";
29             has $pp => (is => 'rw');
30             }
31              
32             sub _uflags {
33 1     1   3 my $self = shift;
34 1         2 my $uflags = 0;
35              
36 1 0 33     7 $uflags |= Linux::Clone::NEWNS if ($self->private_tmp || $self->private_mount || ($self->private_pid && !$self->no_proc));
      0        
      33        
37 1 50       4 $uflags |= Linux::Clone::NEWPID if ($self->private_pid);
38 1 50       3 $uflags |= Linux::Clone::NEWNET if ($self->private_net);
39 1 50       4 $uflags |= Linux::Clone::NEWIPC if ($self->private_ipc);
40 1 50       4 $uflags |= Linux::Clone::NEWUSER if ($self->private_user);
41 1 50       4 $uflags |= Linux::Clone::NEWUTS if ($self->private_uts);
42 1 50       5 $uflags |= Linux::Clone::SYSVSEM if ($self->private_sysvsem);
43              
44 1         996 return $uflags;
45             }
46              
47             sub _subprocess {
48 1     1   4 my ($self, $code, %args) = @_;
49 1 50       4 croak "_subprocess requires a CODE ref" unless ref $code eq 'CODE';
50              
51 1         5 debug "Forking\n";
52             my $pid = Linux::Clone::clone (sub {
53 0     0   0 local $$ = POSIX::getpid(); # try to fix up $$ if we can.
54 0         0 debug "Inside Child $$\n";
55            
56 0         0 $code->(%args);
57 0         0 _exit(0); # always exit with 0
58 1         7 }, 0, POSIX::SIGCHLD | $self->_uflags);
59              
60 1 50       247 croak "Failed to fork: $!" if ($pid < 0);
61              
62 0 0       0 local %SIG = map {my $q=$_; $q => sub {
  0         0  
63 0     0   0 debug "got signal $q in $$\n";
64 0         0 kill 'TERM', $pid;
65 0         0 sleep(0.2);
66 0         0 kill 'KILL', $pid;
67 0         0 kill 'KILL', $pid;
68 0         0 }} ($self->term_child ? @signames : ());
69              
70 0         0 waitpid($pid, 0);
71 0         0 return $?
72             }
73              
74             sub pre_setup {
75 1     1 0 4 my ($self, %args) = @_;
76              
77 1 50       7 croak "Private net is not yet supported" if $self->private_net;
78 1 0 0     8 if ($self->private_pid && (ref $args{code} ne 'CODE' || !$args{_run})) {
      33        
79 0         0 croak "Private PID space requires a coderef to become the new PID 1";
80             }
81             }
82              
83             sub post_setup {
84 0     0 0 0 my ($self, %args) = @_;
85             # If we want a private /tmp, or private mount we need to recursively make every mount private. it CAN be done without that but this is more reliable.
86 0 0 0     0 if ($self->private_tmp || $self->private_mount || ($self->private_pid && !$self->no_proc)) {
      0        
      0        
87 0         0 mount("/", "/", undef, MS_REC|MS_PRIVATE, undef);
88             }
89              
90 0 0       0 if ($self->private_tmp) {
91 0         0 my $data = undef;
92 0 0       0 $data = $self->private_tmp if (ref $self->private_tmp eq 'HASH');
93              
94 0         0 mount("none", "/tmp", "tmpfs", MS_MGC_VAL, undef);
95 0         0 mount("none", "/tmp", "tmpfs", MS_PRIVATE, $data);
96             }
97              
98 0 0 0     0 if ($self->private_pid && !$self->no_proc) {
99 0         0 mount("proc", "/proc", "proc", MS_MGC_VAL, undef);
100 0         0 mount("proc", "/proc", "proc", MS_PRIVATE|MS_REC, undef);
101             }
102             }
103              
104             sub setup {
105 0     0 1 0 my ($self, %args) = @_;
106              
107 0         0 my $uflags = $self->_uflags;
108 0         0 $self->pre_setup(%args);
109            
110 0         0 Linux::Clone::unshare($uflags);
111 0         0 $self->post_setup(%args);
112              
113 0         0 return 1;
114             }
115              
116             sub run {
117 1     1 1 2933 my ($self, %args) = @_;
118              
119 1         6 my $code = $args{code};
120 1         3 $args{_run} = 1;
121              
122 1 50       5 croak "Run must be given a codref to run" unless ref $code eq "CODE";
123              
124 1         15 $self->pre_setup(%args);
125             return $self->_subprocess(sub {
126 0     0     $self->post_setup(%args);
127 0           $code->(%args);
128 1         7 }, %args);
129             }
130              
131             1;
132              
133             __END__