+sub out ($;@) {
+ my $self = shift;
+ return if print { $self->{1} // return } @_; # likely
+ return note_sigpipe($self, 1) if $! == EPIPE;
+ my $err = "error writing to stdout: $!";
+ delete $self->{1};
+ fail($self, $err);
+}
+
+sub puts ($;@) { out(shift, map { "$_\n" } @_) }
+
+sub child_error { # passes non-fatal curl exit codes to user
+ my ($self, $child_error) = @_; # child_error is $?
+ if (my $sock = $self->{sock}) { # send to lei(1) client
+ send($sock, "child_error $child_error", MSG_EOR);
+ } else { # oneshot
+ $self->{child_error} = $child_error;
+ }
+ undef;
+}
+
+sub atfork_prepare_wq {
+ my ($self, $wq) = @_;
+ my $tcafc = $wq->{-ipc_atfork_child_close} //= [ $listener // () ];
+ if (my $sock = $self->{sock}) {
+ push @$tcafc, @$self{qw(0 1 2 3)}, $sock;
+ }
+ if (my $pgr = $self->{pgr}) {
+ push @$tcafc, @$pgr[1,2];
+ }
+ if (my $old_1 = $self->{old_1}) {
+ push @$tcafc, $old_1;
+ }
+ for my $f (qw(lxs l2m)) {
+ my $ipc = $self->{$f} or next;
+ push @$tcafc, grep { defined }
+ @$ipc{qw(-wq_s1 -wq_s2 -ipc_req -ipc_res)};
+ }
+}
+
+sub io_restore ($$) {
+ my ($dst, $src) = @_;
+ for my $i (0..2) { # standard FDs
+ my $io = delete $src->{$i} or next;
+ $dst->{$i} = $io;
+ }
+ for my $i (3..9) { # named (non-standard) FDs
+ my $io = $src->{$i} or next;
+ my @st = stat($io) or die "stat $src.$i ($io): $!";
+ my $f = delete $dst->{"dev=$st[0],ino=$st[1]"} // next;
+ $dst->{$f} = $io;
+ delete $src->{$i};
+ }
+}
+
+sub note_sigpipe { # triggers sigpipe_handler
+ my ($self, $fd) = @_;
+ close(delete($self->{$fd})); # explicit close silences Perl warning
+ send($self->{pkt_op}, '|', MSG_EOR) if $self->{pkt_op};
+ x_it($self, 13);
+}
+
+sub atfork_child_wq {
+ my ($self, $wq) = @_;
+ io_restore($self, $wq);
+ -S $self->{pkt_op} or die 'BUG: {pkt_op} expected';
+ io_restore($self->{l2m}, $wq);
+ %PATH2CFG = ();
+ undef $errors_log;
+ $quit = \&CORE::exit;
+ $current_lei = $self; # for SIG{__WARN__}
+}
+
+sub io_extract ($;@) {
+ my ($obj, @fields) = @_;
+ my @io;
+ for my $f (@fields) {
+ my $io = delete $obj->{$f} or next;
+ my @st = stat($io) or die "W: stat $obj.$f ($io): $!";
+ $obj->{"dev=$st[0],ino=$st[1]"} = $f;
+ push @io, $io;
+ }
+ @io
+}
+
+# usage: ($lei, @io) = $lei->atfork_parent_wq($wq);
+sub atfork_parent_wq {
+ my ($self, $wq) = @_;
+ my $env = delete $self->{env}; # env is inherited at fork
+ my $lei = bless { %$self }, ref($self);
+ for my $f (qw(dedupe ovv)) {
+ my $tmp = delete($lei->{$f}) or next;
+ $lei->{$f} = $wq->deep_clone($tmp);
+ }
+ $self->{env} = $env;
+ delete @$lei{qw(3 -lei_store cfg old_1 pgr lxs)}; # keep l2m
+ my @io = (delete(@$lei{qw(0 1 2)}),
+ io_extract($lei, qw(sock pkt_op startq)));
+ my $l2m = $lei->{l2m};
+ if ($l2m && $l2m != $wq) { # $wq == lxs
+ if (my $wq_s1 = $l2m->{-wq_s1}) {
+ push @io, io_extract($l2m, '-wq_s1');
+ $l2m->{-wq_s1} = $wq_s1;
+ }
+ $l2m->wq_close(1);
+ }
+ ($lei, @io);
+}
+