Remove unneeded internal module now that we use Linux::Clone.
This commit is contained in:
parent
865dc0b66d
commit
58f401e837
5 changed files with 80 additions and 132 deletions
6
Changes
6
Changes
|
@ -1,5 +1,11 @@
|
|||
Revision history for Sys::Linux::Namespaces
|
||||
|
||||
0.013 - May 7 2017
|
||||
* Force target mount option to be an absolute path
|
||||
* Do kill child subprocess on any signal by default. This helps prevent zombie PID 1 processes.
|
||||
* Switch to an existing implementation of the unshare and clone syscall wrappers.
|
||||
* Use the clone syscall instead of unshare+fork when we're running a coderef. This creates fewer processes, and has better handling of CLONE_NEWPID.
|
||||
|
||||
0.012 - May 4 2017
|
||||
* Fix borken mount options
|
||||
|
||||
|
|
|
@ -5,13 +5,24 @@ use strict;
|
|||
use warnings;
|
||||
|
||||
use Sys::Linux::Mount qw/:all/;
|
||||
use Sys::Linux::Unshare qw/:all/;
|
||||
#use Sys::Linux::Unshare qw/:all/;
|
||||
use Linux::Clone;
|
||||
use POSIX qw/_exit/;
|
||||
use Time::HiRes qw/sleep/;
|
||||
|
||||
use Moo;
|
||||
use Carp qw/croak/;
|
||||
|
||||
has no_proc => (is => 'rw');
|
||||
has term_child => (is => 'rw', default => 1);
|
||||
|
||||
our $debug = 0;
|
||||
sub debug {
|
||||
print STDERR @_ if $debug;
|
||||
}
|
||||
|
||||
our $VERSION = v0.013;
|
||||
my @signames = keys %SIG; # capture before anyone has probably localized it.
|
||||
|
||||
for my $p (qw/tmp mount pid net ipc user uts sysvsem/) {
|
||||
my $pp = "private_$p";
|
||||
|
@ -22,13 +33,13 @@ sub _uflags {
|
|||
my $self = shift;
|
||||
my $uflags = 0;
|
||||
|
||||
$uflags |= CLONE_NEWNS if ($self->private_tmp || $self->private_mount || ($self->private_pid && !$self->no_proc));
|
||||
$uflags |= CLONE_NEWPID if ($self->private_pid);
|
||||
$uflags |= CLONE_NEWNET if ($self->private_net);
|
||||
$uflags |= CLONE_NEWIPC if ($self->private_ipc);
|
||||
$uflags |= CLONE_NEWUSER if ($self->private_user);
|
||||
$uflags |= CLONE_NEWUTS if ($self->private_uts);
|
||||
$uflags |= CLONE_SYSVSEM if ($self->private_sysvsem);
|
||||
$uflags |= Linux::Clone::NEWNS if ($self->private_tmp || $self->private_mount || ($self->private_pid && !$self->no_proc));
|
||||
$uflags |= Linux::Clone::NEWPID if ($self->private_pid);
|
||||
$uflags |= Linux::Clone::NEWNET if ($self->private_net);
|
||||
$uflags |= Linux::Clone::NEWIPC if ($self->private_ipc);
|
||||
$uflags |= Linux::Clone::NEWUSER if ($self->private_user);
|
||||
$uflags |= Linux::Clone::NEWUTS if ($self->private_uts);
|
||||
$uflags |= Linux::Clone::SYSVSEM if ($self->private_sysvsem);
|
||||
|
||||
return $uflags;
|
||||
}
|
||||
|
@ -37,16 +48,29 @@ sub _subprocess {
|
|||
my ($self, $code, %args) = @_;
|
||||
croak "_subprocess requires a CODE ref" unless ref $code eq 'CODE';
|
||||
|
||||
my $pid = fork();
|
||||
debug "Forking\n";
|
||||
my $pid = Linux::Clone::clone (sub {
|
||||
local $$ = POSIX::getpid(); # try to fix up $$ if we can.
|
||||
local %SIG = map {$_ => sub {debug "Got signal in $$, exiting"; _exit(0)}} @signames;
|
||||
debug "Inside Child $$\n";
|
||||
|
||||
$code->(%args);
|
||||
_exit(0); # always exit with 0
|
||||
}, 0, POSIX::SIGCHLD | $self->_uflags);
|
||||
|
||||
croak "Failed to fork: $!" if ($pid < 0);
|
||||
if ($pid) {
|
||||
waitpid($pid, 0); # block and wait on child
|
||||
return $?;
|
||||
} else {
|
||||
$code->(%args);
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
my $sighandler =
|
||||
local %SIG = map {my $q=$_; $q => sub {
|
||||
debug "got signal $q in $$\n";
|
||||
kill 'TERM', $pid;
|
||||
sleep(0.2);
|
||||
kill 'KILL', $pid;
|
||||
kill 'KILL', $pid;
|
||||
}} ($self->term_child ? @signames : ());
|
||||
|
||||
waitpid($pid, 0);
|
||||
return $?
|
||||
}
|
||||
|
||||
sub pre_setup {
|
||||
|
@ -85,7 +109,7 @@ sub setup {
|
|||
my $uflags = $self->_uflags;
|
||||
$self->pre_setup(%args);
|
||||
|
||||
unshare($uflags);
|
||||
Linux::Clone::unshare($uflags);
|
||||
$self->post_setup(%args);
|
||||
|
||||
return 1;
|
||||
|
@ -99,21 +123,10 @@ sub run {
|
|||
|
||||
croak "Run must be given a codref to run" unless ref $code eq "CODE";
|
||||
|
||||
$self->pre_setup(%args);
|
||||
$self->_subprocess(sub {
|
||||
$self->pre_setup(%args);
|
||||
my $uflags = $self->_uflags;
|
||||
unshare($uflags);
|
||||
|
||||
# We've just unshared, if we wanted a private pid space we MUST fork again.
|
||||
if ($self->private_pid) {
|
||||
$self->_subprocess(sub {
|
||||
$self->post_setup(%args);
|
||||
$code->(%args);
|
||||
}, %args);
|
||||
} else {
|
||||
$self->setup(%args);
|
||||
$code->(%args);
|
||||
}
|
||||
$self->post_setup(%args);
|
||||
$code->(%args);
|
||||
}, %args);
|
||||
|
||||
return 1;
|
||||
|
@ -186,6 +199,10 @@ Create a private PID namespace. This requires the use of C<< ->run() >>.
|
|||
This requires a C<code> parameter either to C<new()> or to C<setup()>
|
||||
Also sets up a private /proc mount by default
|
||||
|
||||
=item term_child
|
||||
|
||||
Send a term signal to the child process on any signal, followed shortly by a kill signal. This is the default behavior to prevent zombied processes.
|
||||
|
||||
=item no_proc
|
||||
|
||||
Don't setup a private /proc mount when doing private_pid
|
||||
|
|
|
@ -1,74 +0,0 @@
|
|||
package Sys::Linux::Unshare;
|
||||
|
||||
#use strict;
|
||||
use warnings;
|
||||
use Data::Dumper;
|
||||
require Exporter;
|
||||
our @ISA = qw/Exporter/;
|
||||
use Carp qw/croak/;
|
||||
|
||||
require XSLoader;
|
||||
|
||||
XSLoader::load();
|
||||
|
||||
my @unshare_consts = qw/CSIGNAL CLONE_VM CLONE_FS CLONE_FILES CLONE_SIGHAND CLONE_PTRACE CLONE_VFORK CLONE_PARENT CLONE_THREAD CLONE_NEWNS CLONE_SYSVSEM CLONE_SETTLS CLONE_PARENT_SETTID CLONE_CHILD_CLEARTID CLONE_DETACHED CLONE_UNTRACED CLONE_CHILD_SETTID CLONE_NEWCGROUP CLONE_NEWUTS CLONE_NEWIPC CLONE_NEWUSER CLONE_NEWPID CLONE_NEWNET CLONE_IO/;
|
||||
|
||||
our @EXPORT_OK = (@unshare_consts, qw/unshare/);
|
||||
|
||||
our %EXPORT_TAGS = (
|
||||
'consts' => \@unshare_consts,
|
||||
'all' => [@unshare_consts, qw/unshare/],
|
||||
);
|
||||
|
||||
sub clone {
|
||||
my ($flags) = @_;
|
||||
local $! = 0;
|
||||
my $ret_pid = _clone_sys($flags);
|
||||
|
||||
if ($ret_pid < 0) {
|
||||
croak "Clone call failed: $ret_pid $!";
|
||||
}
|
||||
|
||||
return $ret_pid;
|
||||
}
|
||||
|
||||
sub unshare {
|
||||
my ($flags) = @_;
|
||||
|
||||
local $! = 0;
|
||||
my $ret = _unshare_sys($flags);
|
||||
|
||||
if ($ret != 0) {
|
||||
croak "unshare failed $ret $!";
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
use constant {CSIGNAL => 0x000000ff,
|
||||
CLONE_VM => 0x00000100,
|
||||
CLONE_FS => 0x00000200,
|
||||
CLONE_FILES => 0x00000400,
|
||||
CLONE_SIGHAND => 0x00000800,
|
||||
CLONE_PTRACE => 0x00002000,
|
||||
CLONE_VFORK => 0x00004000,
|
||||
CLONE_PARENT => 0x00008000,
|
||||
CLONE_THREAD => 0x00010000,
|
||||
CLONE_NEWNS => 0x00020000,
|
||||
CLONE_SYSVSEM => 0x00040000,
|
||||
CLONE_SETTLS => 0x00080000,
|
||||
CLONE_PARENT_SETTID => 0x00100000,
|
||||
CLONE_CHILD_CLEARTID => 0x00200000,
|
||||
CLONE_DETACHED => 0x00400000,
|
||||
CLONE_UNTRACED => 0x00800000,
|
||||
CLONE_CHILD_SETTID => 0x01000000,
|
||||
CLONE_NEWCGROUP => 0x02000000,
|
||||
CLONE_NEWUTS => 0x04000000,
|
||||
CLONE_NEWIPC => 0x08000000,
|
||||
CLONE_NEWUSER => 0x10000000,
|
||||
CLONE_NEWPID => 0x20000000,
|
||||
CLONE_NEWNET => 0x40000000,
|
||||
CLONE_IO => 0x80000000};
|
||||
|
||||
1;
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
#define PERL_NO_GET_CONTEXT // we'll define thread context if necessary (faster)
|
||||
#define _GNU_SOURCE
|
||||
#include "EXTERN.h" // globals/constant import locations
|
||||
#include "perl.h" // Perl symbols, structures and constants definition
|
||||
#include "XSUB.h" // xsubpp functions and macros
|
||||
#include <sched.h>
|
||||
|
||||
// additional c code goes here
|
||||
|
||||
MODULE = Sys::Linux::Unshare PACKAGE = Sys::Linux::Unshare
|
||||
PROTOTYPES: ENABLE
|
||||
|
||||
# XS code goes here
|
||||
|
||||
# XS comments begin with " #" to avoid them being interpreted as pre-processor
|
||||
# directives
|
||||
|
||||
SV * _unshare_sys(int flags)
|
||||
CODE:
|
||||
ST(0) = sv_newmortal();
|
||||
sv_setiv(ST(0), unshare(flags));
|
||||
|
||||
SV * _clone_sys(int flags)
|
||||
CODE:
|
||||
ST(0) = sv_newmortal();
|
||||
sv_setiv(ST(0), clone(NULL, NULL, CLONE_CHILD_CLEARTID|SIGCHLD|flags, NULL));
|
|
@ -7,6 +7,7 @@ use Test::SharedFork;
|
|||
|
||||
# test 1
|
||||
use Sys::Linux::Namespace;
|
||||
$Sys::Linux::Namespace::debug = 1;
|
||||
|
||||
# test 2
|
||||
SKIP: {
|
||||
|
@ -19,13 +20,37 @@ SKIP: {
|
|||
|
||||
ok(my $pid_ns = Sys::Linux::Namespace->new(private_tmp => 1, private_pid => 1), "Setup pid object");
|
||||
|
||||
$pid_ns->run(code => sub {
|
||||
$ret = $pid_ns->run(code => sub {
|
||||
is($$, 1, "We're init");
|
||||
is_deeply([grep {m|/proc/\d+/|} glob '/proc/*/'], ['/proc/1/'], "Only /proc/1/ exists");
|
||||
});
|
||||
|
||||
ok($ret, "run code in sandbox");
|
||||
|
||||
alarm(5);
|
||||
$pid_ns->run(code => sub {
|
||||
is($$, 1, "Alarmed init");
|
||||
sleep(10);
|
||||
fail("signal propogation didn't happen");
|
||||
});
|
||||
|
||||
alarm(5);
|
||||
$pid_ns->run(code => sub {
|
||||
is($$, 1, "Second alarmed init");
|
||||
|
||||
my $pid = fork();
|
||||
|
||||
isnt($pid, undef, "Fork succeeded");
|
||||
if (!$pid) {
|
||||
sleep(30); # sleep a gigantic amount of time in the child
|
||||
# We should never happen here, because our parent PID 1 should be destroyed by the kernel first
|
||||
fail("Child of PID 1 lived, $$");
|
||||
} else {
|
||||
waitpid($pid, 0); # wait forever
|
||||
fail("PID 1 never got reaped");
|
||||
}
|
||||
});
|
||||
|
||||
ok($namespace->setup(), "Setup namespace in current process");
|
||||
is_deeply([glob "/tmp/*"], [], "No files present in /tmp");
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue