- my $in = $opts->{0} || 0;
- my $out = $opts->{1} || 1;
- my $err = $opts->{2} || 2;
- public_inbox_fork_exec($in, $out, $err, $f, $cmd, \@env);
+ my $redir = [];
+ for my $child_fd (0..2) {
+ my $parent_fd = $opts->{$child_fd};
+ if (defined($parent_fd) && $parent_fd !~ /\A[0-9]+\z/) {
+ defined(my $fd = fileno($parent_fd)) or
+ die "$parent_fd not an IO GLOB? $!";
+ $parent_fd = $fd;
+ }
+ $redir->[$child_fd] = $parent_fd // $child_fd;
+ }
+ my $rlim = [];
+
+ foreach my $l (RLIMITS()) {
+ defined(my $v = $opts->{$l}) or next;
+ my $r = eval "require BSD::Resource; BSD::Resource::$l();";
+ unless (defined $r) {
+ warn "$l undefined by BSD::Resource: $@\n";
+ next;
+ }
+ push @$rlim, $r, @$v;
+ }
+ my $cd = $opts->{'-C'} // ''; # undef => NULL mapping doesn't work?
+ my $pid = pi_fork_exec($redir, $f, $cmd, \@env, $rlim, $cd);
+ $pid < 0 ? undef : $pid;
+}
+
+sub popen_rd {
+ my ($cmd, $env, $opts) = @_;
+ pipe(my ($r, $w)) or die "pipe: $!\n";
+ $opts ||= {};
+ $opts->{1} = fileno($w);
+ my $pid = spawn($cmd, $env, $opts);
+ return unless defined $pid;
+ return ($r, $pid) if wantarray;
+ my $ret = gensym;
+ tie *$ret, 'PublicInbox::ProcessPipe', $pid, $r;
+ $ret;