Found the problem with PID 1
This commit is contained in:
parent
a38af218d7
commit
7447ddb7ff
5 changed files with 64 additions and 52 deletions
|
@ -2,7 +2,7 @@ package Sys::Linux::Mount;
|
||||||
|
|
||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
use Carp qw/carp/;
|
use Carp qw/croak/;
|
||||||
|
|
||||||
require Exporter;
|
require Exporter;
|
||||||
our @ISA = qw/Exporter/;
|
our @ISA = qw/Exporter/;
|
||||||
|
@ -10,37 +10,7 @@ require XSLoader;
|
||||||
|
|
||||||
XSLoader::load();
|
XSLoader::load();
|
||||||
|
|
||||||
my @mount_consts = qw/MS_RDONLY MS_NOSUID MS_NODEV MS_NOEXEC MS_SYNCHRONOUS MS_REMOUNT MS_MANDLOCK MS_DIRSYNC MS_NOATIME MS_NODIRATIME MS_BIND MS_MOVE MS_REC MS_SILENT MS_POSIXACL MS_UNBINDABLE MS_PRIVATE MS_SLAVE MS_SHARED MS_RELATIME MS_KERNMOUNT MS_I_VERSION MS_STRICTATIME MS_LAZYTIME MS_ACTIVE MS_NOUSER/;
|
my @mount_consts = qw/MS_RDONLY MS_NOSUID MS_NODEV MS_NOEXEC MS_SYNCHRONOUS MS_REMOUNT MS_MANDLOCK MS_DIRSYNC MS_NOATIME MS_NODIRATIME MS_BIND MS_MOVE MS_REC MS_SILENT MS_POSIXACL MS_UNBINDABLE MS_PRIVATE MS_SLAVE MS_SHARED MS_RELATIME MS_KERNMOUNT MS_I_VERSION MS_STRICTATIME MS_LAZYTIME MS_ACTIVE MS_NOUSER MS_MGC_VAL/;
|
||||||
|
|
||||||
our @EXPORT_OK = (@mount_consts, qw/mount umount/);
|
|
||||||
|
|
||||||
our %EXPORT_TAGS = (
|
|
||||||
'consts' => \@mount_consts,
|
|
||||||
'all' => [@mount_consts, qw/mount umount/],
|
|
||||||
);
|
|
||||||
|
|
||||||
sub mount {
|
|
||||||
my ($source, $target, $filesystem, $flags, $options_hr) = @_;
|
|
||||||
|
|
||||||
my $options_str = join ',', map {"$_=".$options_hr->{$_}} keys %$options_hr;
|
|
||||||
|
|
||||||
my $ret = _mount_sys($source//"", $target//"", $filesystem//"", $flags//0, $options_str//"");
|
|
||||||
|
|
||||||
if ($ret != 0) {
|
|
||||||
carp "mount failed: $ret $!";
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub umount {
|
|
||||||
my ($target) = @_;
|
|
||||||
|
|
||||||
carp "No argument given to umount()" unless $target;
|
|
||||||
|
|
||||||
my $ret = _umount_sys($target);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
use constant {MS_RDONLY => 1,
|
use constant {MS_RDONLY => 1,
|
||||||
MS_NOSUID => 2,
|
MS_NOSUID => 2,
|
||||||
|
@ -67,6 +37,40 @@ use constant {MS_RDONLY => 1,
|
||||||
MS_STRICTATIME => 1 << 24,
|
MS_STRICTATIME => 1 << 24,
|
||||||
MS_LAZYTIME => 1 << 25,
|
MS_LAZYTIME => 1 << 25,
|
||||||
MS_ACTIVE => 1 << 30,
|
MS_ACTIVE => 1 << 30,
|
||||||
MS_NOUSER => 1 << 31};
|
MS_NOUSER => 1 << 31,
|
||||||
|
MS_MGC_VAL => 0xc0ed0000 # /* Magic flag number to indicate "new" flags */
|
||||||
|
};
|
||||||
|
our @EXPORT_OK = (@mount_consts, qw/mount umount/);
|
||||||
|
|
||||||
|
our %EXPORT_TAGS = (
|
||||||
|
'consts' => \@mount_consts,
|
||||||
|
'all' => [@mount_consts, qw/mount umount/],
|
||||||
|
);
|
||||||
|
|
||||||
|
sub mount {
|
||||||
|
my ($source, $target, $filesystem, $flags, $options_hr) = @_;
|
||||||
|
|
||||||
|
my $options_str;
|
||||||
|
if ($options_hr) {
|
||||||
|
$options_str = join ',', map {"$_=".$options_hr->{$_}} keys %$options_hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
my $ret = _mount_sys($source, $target, $filesystem, $flags//0xc0ed0000, $options_str);
|
||||||
|
|
||||||
|
if ($ret != 0) {
|
||||||
|
croak "mount failed: $ret $!";
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub umount {
|
||||||
|
my ($target) = @_;
|
||||||
|
|
||||||
|
croak "No argument given to umount()" unless $target;
|
||||||
|
|
||||||
|
my $ret = _umount_sys($target);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
|
|
@ -15,7 +15,7 @@ PROTOTYPES: ENABLE
|
||||||
SV *_mount_sys(const char *source, const char *target, const char *filesystem, unsigned long mountflags, const char *data)
|
SV *_mount_sys(const char *source, const char *target, const char *filesystem, unsigned long mountflags, const char *data)
|
||||||
CODE:
|
CODE:
|
||||||
ST(0) = sv_newmortal();
|
ST(0) = sv_newmortal();
|
||||||
sv_setiv(ST(0), mount(source, target, filesystem, mountflags, (const void *) data));
|
sv_setiv(ST(0), mount(source, target, filesystem, mountflags, NULL));
|
||||||
|
|
||||||
SV *_umount_sys(const char *target)
|
SV *_umount_sys(const char *target)
|
||||||
CODE:
|
CODE:
|
||||||
|
|
|
@ -9,7 +9,9 @@ use Sys::Linux::Unshare qw/:all/;
|
||||||
use POSIX qw/_exit/;
|
use POSIX qw/_exit/;
|
||||||
|
|
||||||
use Moo;
|
use Moo;
|
||||||
use Carp qw/carp/;
|
use Carp qw/croak/;
|
||||||
|
|
||||||
|
use Data::Dumper;
|
||||||
|
|
||||||
has no_proc => (is => 'rw');
|
has no_proc => (is => 'rw');
|
||||||
|
|
||||||
|
@ -22,7 +24,7 @@ sub _uflags {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my $uflags = 0;
|
my $uflags = 0;
|
||||||
|
|
||||||
$uflags |= CLONE_NEWNS if ($self->private_tmp || $self->private_mount);
|
$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_NEWPID if ($self->private_pid);
|
||||||
$uflags |= CLONE_NEWNET if ($self->private_net);
|
$uflags |= CLONE_NEWNET if ($self->private_net);
|
||||||
$uflags |= CLONE_NEWIPC if ($self->private_ipc);
|
$uflags |= CLONE_NEWIPC if ($self->private_ipc);
|
||||||
|
@ -35,11 +37,11 @@ sub _uflags {
|
||||||
|
|
||||||
sub _subprocess {
|
sub _subprocess {
|
||||||
my ($self, $code, %args) = @_;
|
my ($self, $code, %args) = @_;
|
||||||
die "_subprocess requires a CODE ref" unless ref $code eq 'CODE';
|
croak "_subprocess requires a CODE ref" unless ref $code eq 'CODE';
|
||||||
|
|
||||||
my $pid = fork();
|
my $pid = fork();
|
||||||
|
|
||||||
carp "Failed to fork: $!" if ($pid < 0);
|
croak "Failed to fork: $!" if ($pid < 0);
|
||||||
if ($pid) {
|
if ($pid) {
|
||||||
waitpid($pid, 0); # block and wait on child
|
waitpid($pid, 0); # block and wait on child
|
||||||
return $?;
|
return $?;
|
||||||
|
@ -52,16 +54,17 @@ sub _subprocess {
|
||||||
sub pre_setup {
|
sub pre_setup {
|
||||||
my ($self, %args) = @_;
|
my ($self, %args) = @_;
|
||||||
|
|
||||||
die "Private net is not yet supported" if $self->private_net;
|
croak "Private net is not yet supported" if $self->private_net;
|
||||||
if ($self->private_pid && (ref $args{code} ne 'CODE' || !$args{_run})) {
|
if ($self->private_pid && (ref $args{code} ne 'CODE' || !$args{_run})) {
|
||||||
die "Private PID space requires a coderef to become the new PID 1";
|
warn Dumper(\%args);
|
||||||
|
croak "Private PID space requires a coderef to become the new PID 1";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub post_setup {
|
sub post_setup {
|
||||||
my ($self, %args) = @_;
|
my ($self, %args) = @_;
|
||||||
# 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.
|
# 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.
|
||||||
if ($self->private_mount || $self->private_tmp) {
|
if ($self->private_tmp || $self->private_mount || ($self->private_pid && !$self->no_proc)) {
|
||||||
mount("/", "/", undef, MS_REC|MS_PRIVATE, undef);
|
mount("/", "/", undef, MS_REC|MS_PRIVATE, undef);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,21 +72,20 @@ sub post_setup {
|
||||||
my $data = undef;
|
my $data = undef;
|
||||||
$data = $self->private_tmp if (ref $self->private_tmp eq 'HASH');
|
$data = $self->private_tmp if (ref $self->private_tmp eq 'HASH');
|
||||||
|
|
||||||
eval {umount("/tmp")}; # ignore it if it wasn't mounted
|
mount("none", "/tmp", "tmpfs", 0, undef);
|
||||||
mount("/tmp", "/tmp", "tmpfs", 0, undef);
|
mount("none", "/tmp", "tmpfs", MS_PRIVATE, $data);
|
||||||
mount("/tmp", "/tmp", "tmpfs", MS_PRIVATE, $data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($self->private_pid && !$self->no_proc) {
|
if ($self->private_pid && !$self->no_proc) {
|
||||||
eval {umount("/proc")}; # ignore it if it wasn't mounted already
|
mount("proc", "/proc", "proc", MS_MGC_VAL, undef);
|
||||||
mount("/proc", "/proc", "proc", 0, undef);
|
mount("proc", "/proc", "proc", MS_PRIVATE|MS_REC, undef);
|
||||||
mount("/proc", "/proc", "proc", MS_PRIVATE, undef);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub setup {
|
sub setup {
|
||||||
my ($self, %args) = @_;
|
my ($self, %args) = @_;
|
||||||
|
|
||||||
|
warn Dumper(\%args);
|
||||||
my $uflags = $self->_uflags;
|
my $uflags = $self->_uflags;
|
||||||
$self->pre_setup(%args);
|
$self->pre_setup(%args);
|
||||||
|
|
||||||
|
@ -99,9 +101,7 @@ sub run {
|
||||||
my $code = $args{code};
|
my $code = $args{code};
|
||||||
$args{_run} = 1;
|
$args{_run} = 1;
|
||||||
|
|
||||||
carp "Run must be given a codref to run" unless ref $code eq "CODE";
|
croak "Run must be given a codref to run" unless ref $code eq "CODE";
|
||||||
my $uflags = $self->_uflags;
|
|
||||||
$self->pre_setup(%args);
|
|
||||||
|
|
||||||
$self->_subprocess(sub {
|
$self->_subprocess(sub {
|
||||||
$self->setup(%args);
|
$self->setup(%args);
|
||||||
|
|
|
@ -5,6 +5,7 @@ use warnings;
|
||||||
use Data::Dumper;
|
use Data::Dumper;
|
||||||
require Exporter;
|
require Exporter;
|
||||||
our @ISA = qw/Exporter/;
|
our @ISA = qw/Exporter/;
|
||||||
|
use Carp qw/croak/;
|
||||||
|
|
||||||
require XSLoader;
|
require XSLoader;
|
||||||
|
|
||||||
|
@ -27,7 +28,7 @@ sub unshare {
|
||||||
my $ret = _unshare_sys($flags);
|
my $ret = _unshare_sys($flags);
|
||||||
|
|
||||||
if ($ret != 0) {
|
if ($ret != 0) {
|
||||||
die "unshare failed $ret $!";
|
croak "unshare failed $ret $!";
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -16,7 +16,14 @@ SKIP: {
|
||||||
my $ret = $namespace->run(code => sub {
|
my $ret = $namespace->run(code => sub {
|
||||||
is_deeply([glob "/tmp/*"], [], "No files present in /tmp");
|
is_deeply([glob "/tmp/*"], [], "No files present in /tmp");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ok(my $pid_ns = Sys::Linux::Namespace->new(private_tmp => 1, private_pid => 1), "Setup pid object");
|
||||||
|
|
||||||
|
$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");
|
ok($ret, "run code in sandbox");
|
||||||
|
|
||||||
ok($namespace->setup(), "Setup namespace in current process");
|
ok($namespace->setup(), "Setup namespace in current process");
|
||||||
|
|
Loading…
Add table
Reference in a new issue