+# Similar to `backtick` or "qx" ("perldoc -f qx"), it calls $qx_cb with
+# the stdout of the given command when done; but respects the given limiter
+# $env is the PSGI env. As with ``/qx; only use this when output is small
+# and safe to slurp.
+sub psgi_qx {
+ my ($self, $env, $limiter, $qx_cb) = @_;
+ my $scalar = '';
+ open(my $qx, '+>', \$scalar) or die; # PerlIO::scalar
+ my $end = sub {
+ my $err = $_[0]; # $!
+ log_err($env, "psgi_qx: $err") if defined($err);
+ finish($self, $env, sub { $qx_cb->(\$scalar) });
+ $qx = undef;
+ };
+ my $rpipe; # comes from popen_rd
+ my $async = $env->{'pi-httpd.async'};
+ my $cb = sub {
+ my ($r, $buf);
+reread:
+ $r = sysread($rpipe, $buf, 65536);
+ if ($async) {
+ $async->async_pass($env->{'psgix.io'}, $qx, \$buf);
+ } elsif (defined $r) {
+ $r ? $qx->write($buf) : $end->();
+ } else {
+ return if $! == EAGAIN; # try again when notified
+ goto reread if $! == EINTR;
+ $end->($!);
+ }
+ };
+ $limiter ||= $def_limiter ||= PublicInbox::Qspawn::Limiter->new(32);
+ $self->start($limiter, sub { # start_cb, may run later, much later...
+ ($rpipe) = @_; # popen_rd result
+ if ($async) {
+ # PublicInbox::HTTPD::Async->new($rpipe, $cb, $end)
+ $async = $async->($rpipe, $cb, $end);
+ # $cb will call ->async_pass or ->close
+ } else { # generic PSGI
+ $cb->() while $qx;
+ }
+ });
+}
+