Remove unneeded internal module now that we use Linux::Clone.

This commit is contained in:
Ryan Voots 2017-05-07 13:22:46 -07:00
parent 865dc0b66d
commit 58f401e837
5 changed files with 80 additions and 132 deletions

View file

@ -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

View file

@ -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

View file

@ -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;

View file

@ -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));

View file

@ -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");
}