From b78dcaaaabf8c6ce76f899c1a83deedf3faca2ac Mon Sep 17 00:00:00 2001 From: Ryan Voots Date: Wed, 3 May 2017 01:38:16 -0700 Subject: [PATCH] Preliminary idea for PID namespaces --- lib/Sys/Linux/Namespace.pm | 69 +++++++++++++++++++++++++++++--------- 1 file changed, 54 insertions(+), 15 deletions(-) diff --git a/lib/Sys/Linux/Namespace.pm b/lib/Sys/Linux/Namespace.pm index a28529a..db553a1 100644 --- a/lib/Sys/Linux/Namespace.pm +++ b/lib/Sys/Linux/Namespace.pm @@ -5,6 +5,7 @@ use warnings; use Sys::Linux::Mount qw/:all/; use Sys::Linux::Unshare qw/:all/; +use POSIX qw/_exit/; sub namespace { my ($options) = @_; @@ -12,8 +13,27 @@ sub namespace { my $uflags = 0; my $mflags = 0; - if ($options->{pid}) { - die "TODO, need to setup a proper 'init' PID 1"; + my $post_setup = sub { + # 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 ($options->{private_mount} || $options->{private_tmp}) { + mount("/", "/", undef, MS_REC|MS_PRIVATE, undef); + } + + if ($options->{private_tmp}) { + if (ref $options->{private_tmp} eq 'HASH') { + mount("/tmp", "/tmp", "tmpfs", MS_PRIVATE, $options->{private_tmp}); + } elsif (ref $options->{private_tmp}) { + die "Bad ref type passed as private_tmp"; + } else { + mount("/tmp", "/tmp", "tmpfs", MS_PRIVATE, undef); + } + } + }; + + if (ref $options->{pid} eq 'CODE') { + $uflags |= CLONE_NEWPID; + } elsif (ref $options->{pid}) { + die "New PID namespace requires a coderef"; } if ($options->{mount} || $options->{private_mount} || $options->{private_tmp}) { @@ -24,20 +44,39 @@ sub namespace { die "TODO, need to setup network interfaces"; } - unshare($uflags); + if (ref $options->{pid} eq 'CODE') { + my $mid_pid = fork(); - # 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 ($options->{private_mount} || $options->{private_tmp}) { - mount("/", "/", undef, MS_REC|MS_PRIVATE, undef); - } + unless($mid_pid == -1) { + if($mid_pid) { + # Original Process + waitpid($mid_pid); # WE MUST BLOCK + return; # don't run anything else in here + } else { + # Middle child process + unshare($uflags); # Setup the namespaces + $post_setup->(); + my $child_pid = fork(); - if ($options->{private_tmp}) { - if (ref $options->{private_tmp} eq 'HASH') { - mount("/tmp", "/tmp", "tmpfs", MS_PRIVATE, $options->{private_tmp}); - } elsif (ref $options->{private_tmp}) { - die "Bad ref type passed as private_tmp"; - } else { - mount("/tmp", "/tmp", "tmpfs", MS_PRIVATE, undef); - } + unless($child_pid == -1) { + if ($child_pid) { + waitpid($child_pid); + } else { + $options->{pid}->(); + } + + # exit and do no cleanup, don't continue running the program, or any END{} blocks + # This is so that we don't cause anything to go wrong in the parent because something was left around + _exit(0); + } else { + die "Couldn't make PID 1: $!"; + } + } + } else { + die "Couldn't fork $!"; + } + } else { + unshare($uflags); + $post_setup->(); } }