PAX
view release on metacpan or search on metacpan
lib/PAX/AppServer.pm view on Meta::CPAN
sub _serve {
my ($self) = @_;
my $image = $self->{image};
_prepare_runtime($image);
my $preload = _preload_modules($image);
unlink $image->{socket_path} if -e $image->{socket_path};
my $server = IO::Socket::UNIX->new(
Type => SOCK_STREAM,
Local => $image->{socket_path},
Listen => 20,
) or die "cannot listen on $image->{socket_path}: $!";
chmod 0600, $image->{socket_path};
local $SIG{TERM} = sub { unlink $image->{socket_path}; exit 0 };
local $SIG{INT} = sub { unlink $image->{socket_path}; exit 0 };
while (my $client = $server->accept) {
my $line = <$client>;
if (!defined $line) {
close $client;
next;
}
my $request = eval { decode_json($line) } // {};
if (($request->{control} // '') eq 'stop') {
print {$client} "__PAX_EXIT__:0\n";
close $client;
last;
}
_run_request($image, $client, $request);
close $client;
}
close $server;
unlink $image->{socket_path};
return 0;
}
sub _daemonize {
my ($self) = @_;
my $pid = fork();
die "fork failed: $!" if !defined $pid;
return 0 if $pid;
setsid();
open STDIN, '<', '/dev/null';
open STDOUT, '>', "$self->{image}{app_dir}/server.log";
open STDERR, '>&', \*STDOUT;
$self->_serve;
exit 0;
}
sub _run_request {
my ($image, $client, $request) = @_;
my $pid = fork();
if (!defined $pid) {
print {$client} "fork failed: $!\n__PAX_EXIT__:111\n";
return;
}
if ($pid == 0) {
open STDOUT, '>&', $client;
open STDERR, '>&', $client;
my $cwd = $request->{cwd} // '.';
chdir $cwd if -d $cwd;
local @ARGV = @{ $request->{argv} // [] };
local $0 = $image->{entrypoint};
$ENV{PAX_APP_IMAGE} = $image->{name};
my $ok = do $image->{entrypoint};
if (!$ok) {
print STDERR defined $@ && length $@ ? $@ : "failed to run $image->{entrypoint}: $!\n";
exit 111;
}
exit 0;
}
waitpid($pid, 0);
my $exit = $? >> 8;
print {$client} "__PAX_EXIT__:$exit\n";
}
sub _prepare_runtime {
my ($image) = @_;
my @libs = @{ $image->{lib_dirs} // [] };
unshift @INC, grep { -d $_ && !_in_inc($_) } @libs;
if (@libs) {
require Config;
my $sep = $Config::Config{path_sep} || ':';
my @existing = grep { defined && length } split /\Q$sep\E/, ($ENV{PERL5LIB} // '');
$ENV{PERL5LIB} = join $sep, @libs, @existing;
}
}
sub _preload_modules {
my ($image) = @_;
my @loaded;
for my $module (@{ $image->{preload_modules} // [] }) {
next if $module !~ /\A[A-Za-z_][A-Za-z0-9_:]*\z/;
my $ok = eval "require $module; 1";
push @loaded, $module if $ok;
}
return \@loaded;
}
sub _direct_exec {
my ($image, $argv) = @_;
_prepare_runtime($image);
my $pid = fork();
die "fork failed: $!" if !defined $pid;
if ($pid == 0) {
my @cmd = ($^X, $image->{entrypoint}, @$argv);
no warnings 'exec';
exec { $cmd[0] } @cmd;
print STDERR "exec failed: $!\n";
exit 111;
}
waitpid($pid, 0);
return $? >> 8;
}
sub _in_inc {
my ($path) = @_;
for my $inc (@INC) {
return 1 if $inc eq $path;
}
return 0;
}
( run in 0.906 second using v1.01-cache-2.11-cpan-5b529ec07f3 )