+sub check_absolute ($$) {
+ my ($var, $val) = @_;
+ die <<EOM if index($val // '/', '/') != 0;
+$var must be an absolute path when using --daemonize: $val
+EOM
+}
+
+sub do_chown ($) {
+ $uid // return;
+ my ($path) = @_;
+ chown($uid, $gid, $path) or warn "chown $path: $!\n";
+}
+
+sub open_log_path ($$) { # my ($fh, $path) = @_; # $_[0] is modified
+ open $_[0], '>>', $_[1] or die "open(>> $_[1]): $!";
+ $_[0]->autoflush(1);
+ do_chown($_[1]);
+}
+
+sub load_mod ($;$) {
+ my ($scheme, $opt) = @_;
+ my $modc = "PublicInbox::\U$scheme";
+ my $mod = $modc.'D';
+ eval "require $mod"; # IMAPD|HTTPD|NNTPD|POP3D
+ die $@ if $@;
+ my %xn;
+ my $tlsd = $xn{tlsd} = $mod->new;
+ my %env = map {
+ substr($_, length('env.')) => $opt->{$_}->[-1];
+ } grep(/\Aenv\./, keys %$opt);
+ $xn{refresh} = sub {
+ my ($sig) = @_;
+ local @ENV{keys %env} = values %env;
+ $tlsd->refresh_groups($sig);
+ };
+ $xn{post_accept} = $tlsd->can('post_accept_cb') ?
+ $tlsd->post_accept_cb : sub { $modc->new($_[0], $tlsd) };
+ my @paths = qw(out err);
+ if ($modc eq 'PublicInbox::HTTP') {
+ @paths = qw(err);
+ $xn{af_default} = 'httpready';
+ if (my $p = $opt->{psgi}) {
+ die "multiple psgi= options specified\n" if @$p > 1;
+ check_absolute('psgi=', $p->[0]) if $daemonize;
+ $tlsd->{psgi} = $p->[0];
+ }
+ }
+ for my $f (@paths) {
+ my $p = $opt->{$f} or next;
+ die "multiple $f= options specified\n" if @$p > 1;
+ check_absolute("$f=", $p->[0]) if $daemonize;
+ $p = File::Spec->canonpath($p->[0]);
+ open_log_path(my $fh, $p);
+ $tlsd->{$f} = $logs{$p} = $fh;
+ }
+ \%xn;
+}
+
+sub daemon_prepare ($$) {
+ my ($default_listen, $xnetd) = @_;