]> Sergey Matveev's repositories - public-inbox.git/commitdiff
qspawn: wire up RLIMIT_* handling to limiters
authorEric Wong <e@80x24.org>
Mon, 11 Mar 2019 23:53:02 +0000 (23:53 +0000)
committerEric Wong <e@80x24.org>
Thu, 4 Apr 2019 09:13:58 +0000 (09:13 +0000)
This allows users to configure RLIMIT_{CORE,CPU,DATA} using
our "limiter" config directive when spawning external processes.

Documentation/public-inbox-config.pod
lib/PublicInbox/Config.pm
lib/PublicInbox/Qspawn.pm
lib/PublicInbox/Spawn.pm

index 9647e4a46cef6924f5127a48532d5f7f6c3d1b0e..dae69987d0276415f0aad32de76d0ef6375d3931 100644 (file)
@@ -228,12 +228,29 @@ large inboxes, it makes sense to put large inboxes on a named
 limiter with a low max value; while smaller inboxes can use
 the default limiter.
 
+C<RLIMIT_*> keys may be set to enforce resource limits for
+a particular limiter.
+
 =over 8
 
 =item publicinboxlimiter.<name>.max
 
 The maximum number of parallel processes for the given limiter.
 
+=item publicinboxlimiter.<name>.rlimitCore
+
+=item publicinboxlimiter.<name>.rlimitCPU
+
+=item publicinboxlimiter.<name>.rlimitData
+
+The maximum core size, CPU time, or data size processes run with the
+given limiter will use.  This may be comma-separated to distinguish
+soft and hard limits.  The word "INFINITY" is accepted as the
+RLIM_INFINITY constant (if supported by your OS).
+
+See L<setrlimit(2)> for more info on the behavior of RLIMIT_CORE,
+RLIMIT_CPU, and RLIMIT_DATA for you operating system.
+
 =back
 
 =head3 EXAMPLE WITH NAMED LIMITERS
index ec9a469c3516183da8459336751571fdfd6ef12e..6f62712f0eb1ea43b645e52a4a7fabe3c0cc1e9c 100644 (file)
@@ -133,7 +133,9 @@ sub limiter {
        $self->{-limiters}->{$name} ||= do {
                require PublicInbox::Qspawn;
                my $max = $self->{"publicinboxlimiter.$name.max"};
-               PublicInbox::Qspawn::Limiter->new($max);
+               my $limiter = PublicInbox::Qspawn::Limiter->new($max);
+               $limiter->setup_rlimit($name, $self);
+               $limiter;
        };
 }
 
index 509a441246e7407932a4db62d252e8d46caa392f..79cdae711410d769f07c6e21c491a2c9495952c6 100644 (file)
@@ -43,10 +43,18 @@ sub new ($$$;) {
 sub _do_spawn {
        my ($self, $cb) = @_;
        my $err;
+       my ($cmd, $env, $opts) = @{$self->{args}};
+       my %opts = %{$opts || {}};
+       my $limiter = $self->{limiter};
+       foreach my $k (PublicInbox::Spawn::RLIMITS()) {
+               if (defined(my $rlimit = $limiter->{$k})) {
+                       $opts{$k} = $rlimit;
+               }
+       }
 
-       ($self->{rpipe}, $self->{pid}) = popen_rd(@{$self->{args}});
+       ($self->{rpipe}, $self->{pid}) = popen_rd($cmd, $env, \%opts);
        if (defined $self->{pid}) {
-               $self->{limiter}->{running}++;
+               $limiter->{running}++;
        } else {
                $self->{err} = $!;
        }
@@ -251,9 +259,38 @@ sub new {
                max => $max || 32,
                running => 0,
                run_queue => [],
+               # RLIMIT_CPU => undef,
+               # RLIMIT_DATA => undef,
+               # RLIMIT_CORE => undef,
        }, $class;
 }
 
+sub setup_rlimit {
+       my ($self, $name, $config) = @_;
+       foreach my $rlim (PublicInbox::Spawn::RLIMITS()) {
+               my $k = lc($rlim);
+               $k =~ tr/_//d;
+               $k = "publicinboxlimiter.$name.$k";
+               defined(my $v = $config->{$k}) or next;
+               my @rlimit = split(/\s*,\s*/, $v);
+               if (scalar(@rlimit) == 1) {
+                       push @rlimit, $rlimit[0];
+               } elsif (scalar(@rlimit) != 2) {
+                       warn "could not parse $k: $v\n";
+               }
+               eval { require BSD::Resource };
+               if ($@) {
+                       warn "BSD::Resource missing for $rlim";
+                       next;
+               }
+               foreach my $i (0..$#rlimit) {
+                       next if $rlimit[$i] ne 'INFINITY';
+                       $rlimit[$i] = BSD::Resource::RLIM_INFINITY();
+               }
+               $self->{$rlim} = \@rlimit;
+       }
+}
+
 # captures everything into a buffer and executes a callback when done
 package PublicInbox::Qspawn::Qx;
 use strict;
index 202cfcaebee7cba63ccabc68378dd3464754220e..fd9816082188dc8265bdad89a1449e5e9cbe73ee 100644 (file)
@@ -18,6 +18,7 @@ use Symbol qw(gensym);
 use IO::Handle;
 use PublicInbox::ProcessPipe;
 our @EXPORT_OK = qw/which spawn popen_rd/;
+sub RLIMITS () { qw(RLIMIT_CPU RLIMIT_CORE RLIMIT_DATA) }
 
 my $vfork_spawn = <<'VFORK_SPAWN';
 #include <sys/types.h>
@@ -202,7 +203,7 @@ sub spawn ($;$$) {
        my $err = $opts->{2} || 2;
        my $rlim = [];
 
-       foreach my $l (qw(RLIMIT_CPU RLIMIT_CORE RLIMIT_DATA)) {
+       foreach my $l (RLIMITS()) {
                defined(my $v = $opts->{$l}) or next;
                my ($soft, $hard);
                if (ref($v)) {