]> git.ipfire.org Git - thirdparty/public-inbox.git/log
thirdparty/public-inbox.git
21 hours agosearchidxshard: drop unused `echo' sub master
Eric Wong [Sat, 13 Sep 2025 07:40:14 +0000 (07:40 +0000)] 
searchidxshard: drop unused `echo' sub

We no longer need a separate `echo' command to check the
doneness of a previous ->ipc_do command since we've made
->ipc_do async and introduced ->ipc_wait_all in commit
f0b9f90a (*index: propagate exceptions from shard processes, 2025-09-04)

2 days agosearchidx: use warn for excessively long terms
Eric Wong [Sat, 13 Sep 2025 12:21:50 +0000 (12:21 +0000)] 
searchidx: use warn for excessively long terms

Filename and linenumber context information from Carp::carp is
unlikely to be useful here on (likely) worthless data from bad
MUAs.  carp adds needless noise for users tracking down actual
code bugs with PERL5OPT=-MCarp=verbose, which changes Carp::carp
into Carp::cluck.

2 days agot/httpd-corner: fix test on missing curl-config(1)
Eric Wong [Fri, 12 Sep 2025 21:28:49 +0000 (21:28 +0000)] 
t/httpd-corner: fix test on missing curl-config(1)

We were misusing our internal context-dependent `require_cmd',
which requires the caller to call `skip' explicitly when a
return value is desired.  So add a new `skip' call for testers
without curl-config(1).  Furthermore, the `SKIP' label was
incorrectly placed for Test::More::skip to use, so create an
explicit block for it.

Reported-by: Jonathan Corbet <corbet@lwn.net>
Link: https://public-inbox.org/meta/878qikobes.fsf@trenco.lwn.net/
3 days agoextindex: fix --reindex
Eric Wong [Fri, 12 Sep 2025 23:28:18 +0000 (23:28 +0000)] 
extindex: fix --reindex

`public-inbox-extindex --reindex' deprioritizes itself for
public-inbox-extindex invocations without --reindex by shutting
down shard processes to let other processes acquire the lock and
process new messages, first.

Restarting shard processes during --reindex was causing new
Xapian shards to be written to v2 inboxes instead of the
extindex itself.  This bug was introduced with the
simplifications to internal data structures to eliminate the
ad-hoc $sync structure.

The local-ized use of ExtSearchIdx->{ibx} tricked
PublicInbox::SearchIdxShard::new into using the standard v2 code
path.  So make SearchIdxShard->new check the `$v2w' object for
the ability to call `eidx_sync' rather than the existence of the
{ibx} field.

I only noticed this bug while working on the --split-shards
feature for performance.

Fixes: 922b765d ((ext)index: move {max_size} and related bits to $self, 2025-01-10)
5 days agoipc: improve exception handling
Eric Wong [Sun, 7 Sep 2025 23:41:44 +0000 (23:41 +0000)] 
ipc: improve exception handling

When an exception triggers a teardown of the worker process
(ipc_worker_stop), we need to combine subsequent exceptions and
show the original one, first.  In other words, we must not lose
the original exception if new exceptions are thrown during
teardown.

So rely on `wantarray' to grab caller contexts to allow
returning exceptions as a list rather than throwing them
immediately.

5 days agoipc: avoid context line in generated exception
Eric Wong [Sun, 7 Sep 2025 23:41:43 +0000 (23:41 +0000)] 
ipc: avoid context line in generated exception

The filename and line number of the "aborted" message is
needless noise and confusing when dealing with errors
which already triggered ipc_fail.  So add a newline to
ensure Perl doesn't add context information if it needs
to `die' or `warn' on that message.

8 days agodoc updates
Eric Wong [Sun, 7 Sep 2025 10:00:19 +0000 (10:00 +0000)] 
doc updates

9 days ago*index: increase default --commit-interval to 15s
Eric Wong [Sat, 6 Sep 2025 19:45:06 +0000 (19:45 +0000)] 
*index: increase default --commit-interval to 15s

The DBD::SQLite(3pm) sqlite_busy_timeout is 30s and we don't
currently have a way to override this.  Thus 15s should be
adequate assuming we can keep SQLite commit times <10s.
Currently, the worst case commit times can exceed even 30s for
Xapian, but that doesn't affect over.sqlite3 which commits
fairly quickly.

9 days agoreject_bots: avoid download prompts in Firefox
Eric Wong [Tue, 2 Sep 2025 20:30:14 +0000 (20:30 +0000)] 
reject_bots: avoid download prompts in Firefox

Apparently, some versions of Firefox will open a download prompt
when attempting to open the page without a Content-Type.  So set
a Content-Type and keep those installations and users of Firefox
happy.

Reported-by: Leah Neukirchen <leah@vuxu.org>
Link: https://public-inbox.org/meta/878qiwriq7.fsf@vuxu.org/
9 days agoreject_bots: allow .well-known unconditionally
Eric Wong [Tue, 2 Sep 2025 20:30:13 +0000 (20:30 +0000)] 
reject_bots: allow .well-known unconditionally

Allowing */.well-known/* allows Let's Encrypt (and likely
similar) services to access static files for ACME validation
during the automated TLS certificate renewal process.

9 days agoreject_bots: delay app call
Eric Wong [Tue, 2 Sep 2025 20:30:12 +0000 (20:30 +0000)] 
reject_bots: delay app call

We can save some cycles by delaying the potentially expensive
app invocation until after the PATH_INFO + user-agent check.

9 days ago*index: propagate exceptions from shard processes
Eric Wong [Thu, 4 Sep 2025 19:22:27 +0000 (19:22 +0000)] 
*index: propagate exceptions from shard processes

We'll introduce a new ->ipc_async internal API and use it for
->ipc_do calls where the return value is ignored.  This new
API is modeled after our async API for accessing
`git cat-file --batch*' and remains compatible with synchronous
callers who want the return value of ->ipc_do.

Processes no longer spin and burn CPU after hitting ENOSPC or
dealing with database or FS corruption.  Instead, they should
now die properly in most cases.  However, hangs and crashes may
still possible since Xapian may abort(3) in some ENOSPC cases
and SQLite's may trigger SIGBUS via mmap(2) (if using WAL or
forcing mmap use).

9 days agoipc: add comment on pipe usage (vs socketpair)
Eric Wong [Thu, 4 Sep 2025 19:22:26 +0000 (19:22 +0000)] 
ipc: add comment on pipe usage (vs socketpair)

Using pipes here isn't ideal for developer ergonomics, but the
increased buffer size for non-privileged processes on Linux is
still worth the extra performance with expensive indexing done
in each shard.

9 days agoipc: use read_all to deal with short reads
Eric Wong [Thu, 4 Sep 2025 19:22:25 +0000 (19:22 +0000)] 
ipc: use read_all to deal with short reads

The `read' perlop can still return short reads with the default
IO layer on pipes.

12 days agov2writable: don't attempt to print defrag progress w/o -v
Eric Wong [Tue, 2 Sep 2025 07:21:18 +0000 (07:21 +0000)] 
v2writable: don't attempt to print defrag progress w/o -v

Otherwise we'd crash when attempting to use `undef' as a
subroutine reference :x

2 weeks agoipc: drop -wq_ppid field
Eric Wong [Fri, 29 Aug 2025 20:38:55 +0000 (20:38 +0000)] 
ipc: drop -wq_ppid field

It's not needed anymore nowadays since we use attach_pid for IO
objects.

2 weeks agoipc: remove {-ipc_pid} field
Eric Wong [Fri, 29 Aug 2025 20:38:54 +0000 (20:38 +0000)] 
ipc: remove {-ipc_pid} field

The fork generation is already attached to the response pipe,
so there's no need to keep the PID around and do comparisons
against the getpid(2) result.

2 weeks agot/ipc: enable autoflush for warnings
Eric Wong [Fri, 29 Aug 2025 20:38:53 +0000 (20:38 +0000)] 
t/ipc: enable autoflush for warnings

I have no idea what I was thinking when I disabled autoflush in
this test, but it should make the test more reliable when
checking warnings.

2 weeks ago*index: support --commit=$SECONDS to adjust the commit interval
Eric Wong [Fri, 29 Aug 2025 19:04:01 +0000 (19:04 +0000)] 
*index: support --commit=$SECONDS to adjust the commit interval

The default 5s interval is too low for initial indexing with
gian inboxes or extindices which aren't ready for public
consumption, yet.  It's also too low for --wal users since
WAL mode of SQLite (by default) allows readers to proceed
without waiting on a writer.

I'm not sure what the defaults should be, so allow users to
change it, for now.

2 weeks agodoc: add some notes on bitrot avoidance
Eric Wong [Tue, 26 Aug 2025 20:21:16 +0000 (20:21 +0000)] 
doc: add some notes on bitrot avoidance

2 weeks agosupport --cow switch to preserve CoW on btrfs
Eric Wong [Tue, 26 Aug 2025 19:50:52 +0000 (19:50 +0000)] 
support --cow switch to preserve CoW on btrfs

We currently unconditionally disable CoW on btrfs to reduce
fragmentation.  Unfortunately, disabling CoW may cause data
corruption on all btrfs RAID levels, so provide an option to
keep it enabled.  In the future, CoW may become the default on
btrfs (matching the FS default) even if fragmentation is awful.

2 weeks agoavoid redundant $creat_opt hashref in some places
Eric Wong [Tue, 26 Aug 2025 19:50:51 +0000 (19:50 +0000)] 
avoid redundant $creat_opt hashref in some places

We can use the Getopt::Long hashref directly in some of
these places to reduce cognitive overhead for understanding
our internal data structures.

2 weeks agotest_common: delete wal param when creating inbox
Eric Wong [Tue, 26 Aug 2025 19:50:50 +0000 (19:50 +0000)] 
test_common: delete wal param when creating inbox

We don't want the `{wal}' field leaking into the Inbox object,
it only needs to be in the hashref populated by Getopt::Long.

2 weeks agouse Getopt::Long hashref for --no-fsync and --dangerous
Eric Wong [Tue, 26 Aug 2025 19:50:49 +0000 (19:50 +0000)] 
use Getopt::Long hashref for --no-fsync and --dangerous

We can eliminate the {-no_fsync} and {-dangerous} fields by
using the Getopt::Long hashref directly.  This will make it
easier to support additional CLI options (e.g. --cow).

Our internal APIs now defaults to disabling fsync, however the
CLI tools still override that internal default to enable fsync.
Having our internals default to disabling fsync can slightly
improve test performance, since they're the main users of our
unstable internal API.

2 weeks agooveridx: take Getopt::Long options hashref directly
Eric Wong [Tue, 26 Aug 2025 19:50:48 +0000 (19:50 +0000)] 
overidx: take Getopt::Long options hashref directly

Taking the Getopt::Long options hashref directly will allow us
to support future options more easily and avoid copying/mapping
fields (e.g. {-no_fsync}, {journal_mode}) across different
object types).

2 weeks agot/lei_store: ensure over.sqlite3 uses WAL
Eric Wong [Tue, 26 Aug 2025 19:50:47 +0000 (19:50 +0000)] 
t/lei_store: ensure over.sqlite3 uses WAL

As lei is a single user store, it's always used WAL since it
should improve parallelism and I/O patterns.  An additional test
here will ensure bugs don't slip in where we forget to enable
WAL.

2 weeks agomsgmap: take Getopt::Long options hashref for read-write DBs
Eric Wong [Tue, 26 Aug 2025 19:50:46 +0000 (19:50 +0000)] 
msgmap: take Getopt::Long options hashref for read-write DBs

Another step towards making it easier to support more SQLite and
Xapian-specific options on the CLI for tuning.

2 weeks agosearchidx: take Getopt::Long options hashref on create
Eric Wong [Tue, 26 Aug 2025 19:50:45 +0000 (19:50 +0000)] 
searchidx: take Getopt::Long options hashref on create

Relying more on the hashref populated by Getopt::Long should
reduce cognitive overhead.  Future commits will allow us to
eliminate the {-no_fsync} and {-dangerous} fields and allow
us to more easily support new switches for toggling CoW/No_COW,
and other options for SQLite and Xapian tuning.

2 weeks agot/ipc: improve test reliability
Eric Wong [Tue, 26 Aug 2025 19:50:44 +0000 (19:50 +0000)] 
t/ipc: improve test reliability

->wq_close works asynchronously, nowadays, so processes may
not be completely done writing warnings when the parent process
reads the file.  We'll test some untested methods after test_die
to ensure the worker has had time to write the exception to the
warning log.  Finally, we'll explain warning contents on failure
in case it happens again.

2 weeks agolei_saved_search: avoid //= on complex hashref assignment
Eric Wong [Tue, 26 Aug 2025 19:50:43 +0000 (19:50 +0000)] 
lei_saved_search: avoid //= on complex hashref assignment

Setting $self->{oidx} via `//=' may be unsafe due to
->lock_for_scope setting $self->{lock_fh}.  While this is not
known to cause problems at the moment, it may be problematic in
future as we've had to deal with subtle bugs in similar code in
the past.

2 weeks agoinit: store Getopt::Long options in hashref
Eric Wong [Tue, 26 Aug 2025 19:50:42 +0000 (19:50 +0000)] 
init: store Getopt::Long options in hashref

We will pass this options hashref freely across internal
backends, so using a hashref is more consistent with the rest
of our codebase and allows eliminating some local variables.

2 weeks agoextindex|v2: defrag SQLite and Xapian DBs on btrfs
Eric Wong [Tue, 26 Aug 2025 19:50:41 +0000 (19:50 +0000)] 
extindex|v2: defrag SQLite and Xapian DBs on btrfs

Doing periodic defrags ought to improve performance and perhaps
allow CoW to be usable with btrfs.  The autodefrag mount option
of btrfs(5) doesn't seem recommended by btrfs developers since
it's too aggressive, defragments too often, and wears out devices.
Performing defrag on our end should allow users to tune a more
ideal defrag interval to maintain performance while avoiding
excessive device wear.

2 weeks agoextindex: reduce IPC and Xapian updates on reindex
Eric Wong [Tue, 26 Aug 2025 19:50:40 +0000 (19:50 +0000)] 
extindex: reduce IPC and Xapian updates on reindex

Instead of updating the document and re-adding eidx keys +
List-IDs repeatedly, we can do it at once.  Doing so reduces
IPC traffic and ought to reduce FS traffic on the Xapian DB.

2 weeks agov2writable: checkpoint: clarify $dbh is for msgmap
Eric Wong [Tue, 26 Aug 2025 19:50:39 +0000 (19:50 +0000)] 
v2writable: checkpoint: clarify $dbh is for msgmap

We overload the checkpoint sub for -extindex, however only v2 has
msgmap.sqlite3 so we'll avoid confusing ourselves with with a
generic `$dbh' name in favor of `$mm_dbh'.

2 weeks agov2writable: show git wait time and abbreviation legend
Eric Wong [Tue, 26 Aug 2025 19:50:38 +0000 (19:50 +0000)] 
v2writable: show git wait time and abbreviation legend

It's probably important to note wait times necessary for
->async_wait_all, even if it doesn't tell us the times for git
requests we didn't wait on.

3 weeks ago*index: ignore misordered References if In-Reply-To exists
Eric Wong [Fri, 22 Aug 2025 20:40:25 +0000 (20:40 +0000)] 
*index: ignore misordered References if In-Reply-To exists

Fix our indexers to favor In-Reply-To during the Message-ID
unique-fication phase.  Some MUAs will generate an incorrect
References header ordering and put its direct In-Reply-To at the
head of the References list instead of at the tail.

Having the same Message-ID in both References and In-Reply-To
inadvertently caused the In-Reply-To value to get dropped by the
`uniq_mids' subroutine.  To fix this, we reverse the values
before `uniq_mids' and reverse the result again since
PublicInbox::SearchThread (and Mail::Thread before) depends on
the In-Reply-To being last.

An expensive(*) --rethread --reindex will be required to fix
this on the existing data set.  I've only tested this change
with a three message inbox consisting of the following
Message-IDs:

<153126248868.14533.9751473662727327569.stgit@warthog.procyon.org.uk>
<CA+55aFzEjPUGZFk7PnM0T6YEn5uRrscgyCHyhc_cYz0m8ejdLA@mail.gmail.com>
<29128.1531356361@warthog.procyon.org.uk>

Where <29128.1531356361@warthog.procyon.org.uk> is the message
with the out-of-order References header.

(*) - reindexing lore/all would take days or even a week :<

Reported-by: Askar Safin <safinaskar@zohomail.com>
Link: https://public-inbox.org/meta/198d28845f7.bd9650eb4485.7082558303836689927@zohomail.com/
Link: https://lore.kernel.org/linux-fsdevel/29128.1531356361@warthog.procyon.org.uk/raw
3 weeks agov2+extindex: show commit time and indexing rate in progress
Eric Wong [Tue, 19 Aug 2025 00:33:40 +0000 (00:33 +0000)] 
v2+extindex: show commit time and indexing rate in progress

With the `-v' switch, we'll display these rates to track total
indexing rate and commit speeds throughout the indexing phase.
These numbers will help us monitor for slowdowns throughout the
entirety of a large indexing job taking several days.  This
change may help us decide whether or not to start implementing
autodefrag for btrfs and similar CoW FSes prone to performance
degradation from fragmentation.

3 weeks agoextsearchidx: distinguish between binary and hex OIDs
Eric Wong [Tue, 19 Aug 2025 00:33:39 +0000 (00:33 +0000)] 
extsearchidx: distinguish between binary and hex OIDs

Meaningful variables make it easier for hackers to understand
the code, since this is one of the few places where we freely
mix human-readable hex and binary representations of OIDs.

3 weeks agodoc: publicinbox.*.{altid,indexheader} updates for 2.0+
Eric Wong [Tue, 19 Aug 2025 00:33:38 +0000 (00:33 +0000)] 
doc: publicinbox.*.{altid,indexheader} updates for 2.0+

These were added a year ago but I forgot to update the
manpage and release notes :x  And my brain still fails
to put a release together :<

3 weeks agot/ipc.t: streamline dependency check
Eric Wong [Tue, 19 Aug 2025 00:33:37 +0000 (00:33 +0000)] 
t/ipc.t: streamline dependency check

We should not be checking for Socket::Msghdr or Inline::C
presence since there is now a 3rd way to support SCM_RIGHTS
passing with the syscall, pack, and unpack perlops.

3 weeks agospawn: add note about dropping SCM_RIGHTS in Inline::C
Eric Wong [Tue, 19 Aug 2025 00:33:36 +0000 (00:33 +0000)] 
spawn: add note about dropping SCM_RIGHTS in Inline::C

Inline::C seems more difficult to support in a hypothetical
alternative implementation of Perl which maintains and
distributes implementation-specific ports of XS modules.

3 weeks agotest_common: fix +SCM_RIGHTS check
Eric Wong [Tue, 19 Aug 2025 00:33:35 +0000 (00:33 +0000)] 
test_common: fix +SCM_RIGHTS check

We need to ensure we don't attempt to load a module named
`+SCM_RIGHTS' by skipping it after expansion to module names

Furthermore, start deprioritizing Inline::C in the help text
since it'll be difficult to support in alternative
implementations of Perl.

3 weeks agoipc: get rid of -ipc_ppid field in favor of fork_gen
Eric Wong [Tue, 19 Aug 2025 00:33:34 +0000 (00:33 +0000)] 
ipc: get rid of -ipc_ppid field in favor of fork_gen

We own all the `fork' calls in our codebase, so rely on the
existing $PublicInbox::OnDestroy::fork_gen instead of using `$$'
to call getpid(2) everywhere.  getpid(2) is slower nowadays
since it's always a syscall on modern glibc whereas it was
cached (somewhat unsafely) in the past.

3 weeks agoipc: use PublicInbox::IO::attach_pid instead of awaitpid
Eric Wong [Tue, 19 Aug 2025 00:33:33 +0000 (00:33 +0000)] 
ipc: use PublicInbox::IO::attach_pid instead of awaitpid

Blessing and attaching PIDs to PublicInbox::IO objects will give
us access to my_readline and my_bufread subs to allow for
asynchronous callbacks for error handling (similar to
PublicInbox::Git->cat_async).

3 weeks agouse autodie in more places (extindex, pop3d, ipc)
Eric Wong [Tue, 19 Aug 2025 00:33:32 +0000 (00:33 +0000)] 
use autodie in more places (extindex, pop3d, ipc)

Another gradual step towards autodie-ification of our codebase.

4 weeks agoextindex: preserve indexlevel=basic on incremental update
Eric Wong [Fri, 15 Aug 2025 13:41:36 +0000 (13:41 +0000)] 
extindex: preserve indexlevel=basic on incremental update

-extindex needs to preserve indexlevel=basic when doing
incremental updates if the extindex was originally created with
indexlevel=basic.  Otherwise blindly upgrading somebody to
indexlevel=full would waste disk space and likely result in
inconsistent indexing on the Xapian side.

Fixes: bf2360b31 (extindex: support `-L basic' to avoid most Xapian space, 2025-08-13)
4 weeks agoinit|*index|convert: --wal enables Write-Ahead Log in SQLite
Eric Wong [Fri, 15 Aug 2025 13:41:35 +0000 (13:41 +0000)] 
init|*index|convert: --wal enables Write-Ahead Log in SQLite

SQLite WAL <https://sqlite.org/wal.html> ought to improve
concurrency for readers when writers are active.  While users
always had the option of enabling it with the sqlite3(1) tool,
having a `--wal' switch may encourage its use.

It's already on by default for lei/store and mail_sync.sqlite3
where readers and writers are the same user, but enabling it by
default for public-facing daemons would cause permissions
problems so it remains optional.

The key downside of WAL is readers (-(netd|httpd|imapd|nntpd|pop3d)
require write access to the directories containing over.sqlite3
and msgmap.sqlite3).  In the past, public-facing read-only
daemons were not expected to ever have write permissions to
inbox and extindex directories, but WAL requires writability
since SQLite||DBD::SQLite currently doesn't provide a way to
guarantee the persistence of -wal and -shm files, especially
when accessed by sqlite3(1) or 3rd-party scripts.

The main advantage of supporting WAL is so the writers won't
block readers, avoiding busy timeouts on the read side and
reducing the need for writers to commit transactions
periodically to keep readers happy (currently a ridiculously
short 5s).  We can investigate higher commit intervals (or
eliminating them entirely) for users of WAL.

WAL should also reduce fragmentation in btrfs and similar CoW
filesystems by reducing fsync(2) calls, this remains to be
proven as measuring such changes takes a while and I don't
want to put more wear on my SSD.

4 weeks agoextindex: support `-L basic' to avoid most Xapian space
Eric Wong [Wed, 13 Aug 2025 21:42:48 +0000 (21:42 +0000)] 
extindex: support `-L basic' to avoid most Xapian space

For users uninterested in being able to search message contents,
the `basic' indexlevel is now available.

The /misc/ Xapian index for data on individual inboxes (e.g. names,
descriptions, email addresses) remains even with `basic'; but
individual messages are not indexed.

4 weeks agoextindex: improve error checking with try_cat
Eric Wong [Wed, 13 Aug 2025 21:42:47 +0000 (21:42 +0000)] 
extindex: improve error checking with try_cat

PublicInbox::IO::try_cat will detect read(2) errors properly
instead of relying on the readline perlop for error checking.

4 weeks agoextindex: reduce IPC for cross-posted messages
Eric Wong [Wed, 13 Aug 2025 21:42:46 +0000 (21:42 +0000)] 
extindex: reduce IPC for cross-posted messages

When dealing with cross-posted messages, we can reduce IPC
traffic by only sending the List-ID header(s) across the pipe
instead of all headers of a message.  List-ID headers only take
up a small portion of all message headers so the IPC traffic
reduction can be helpful in saving memory bandwidth to improve
performance.

This change will also make it easier to journal work for Xapian
and perform all work for cross posted messages in the same
transaction.  This ought to reduce doing updates for a given
document across transactions and hopefully reduce storage device
wear while improving performance.

5 weeks agolei: input: warn on `L:' and `t:' use consistently
Eric Wong [Sat, 9 Aug 2025 01:02:11 +0000 (01:02 +0000)] 
lei: input: warn on `L:' and `t:' use consistently

When using `--in-format/-F', we need to bail out on the /\A(?:L|kw):/
check as early as possible before checking for `$urlish_scheme:' vs.
`--in-format' conflicts.  Otherwise, we'd get a confusing error
message such as: --in-format=mboxrd and `l:' conflict

5 weeks agonodatacow: warn about CoW being disabled on btrfs
Eric Wong [Thu, 7 Aug 2025 00:50:05 +0000 (00:50 +0000)] 
nodatacow: warn about CoW being disabled on btrfs

While Xapian and SQLite performance is untenable with CoW on
btrfs, disabling CoW is not without caveats on btrfs.  So warn
users about the possibility of data corruption.

5 weeks ago*index: don't try to index boolean terms >245 bytes long
Eric Wong [Thu, 7 Aug 2025 00:49:31 +0000 (00:49 +0000)] 
*index: don't try to index boolean terms >245 bytes long

Xapian's flint, chert, and glass backends only support a maximum
term length of 245 bytes including the upper-case term prefix.
Thus we can't index things like insanely long Message-IDs in
References: or overly long pathnames or newsgroup names.

The work-in-progress honey backend still doesn't support
updates, yet, (only created from an existing glass DB), and I
doubt the limit will increase since excessive term lengths are
usually a mistake of mangled whitespace or a broken/spammy
client somewhere.

6 weeks agoTODO: add more things to fix ASAP
Eric Wong [Sun, 27 Jul 2025 23:17:41 +0000 (23:17 +0000)] 
TODO: add more things to fix ASAP

More things discovered wrong while recovering slowly from broken
hardware :x  This feels overwhelming :<

8 weeks agopsgi_rproxy: standardize on 64K I/O size
Eric Wong [Fri, 18 Jul 2025 01:39:42 +0000 (01:39 +0000)] 
psgi_rproxy: standardize on 64K I/O size

Much of our bulk I/O in DS and IO.pm already uses 64K buffers
(matching the Linux default pipe size) instead of 16K buffers.
While larger I/O sizes can result in more work for the malloc
implementation, they can also reduce syscall and Perl function
call overhead to reduce CPU usage and improve throughput on long
fat networks (LFNs).  Furthermore, standardizing on larger sizes
ought to reduce fragmentation since malloc can avoid splitting
existing buffers used for bulk I/O we do in other places.

2 months agocontrib: PSGI RejectBots middleware
Eric Wong [Sun, 13 Jul 2025 00:08:36 +0000 (00:08 +0000)] 
contrib: PSGI RejectBots middleware

RejectBots rejects certain user-agents and forces meta-refresh
while requiring the use of persistent connections.  While the
first two techniques are relatively well-known, the persistent
connection requirement requires the public-inbox-(netd|httpd) to
be directly connected with the remote client without something
like (haproxy|nginx) in front of it.

This middleware is used by public-inbox.org and the
yhbt.net/lore mirror.

2 months agohttp: introduce a reverse proxy PSGI app
Eric Wong [Sun, 13 Jul 2025 00:08:35 +0000 (00:08 +0000)] 
http: introduce a reverse proxy PSGI app

While nginx is (and is likely to remain) a popular reverse
proxy, it is a commercial open core project nowadays which I
haven't used in production in over a decade (since I used a Ruby
reverse proxy, instead).  Furthermore, nginx|haproxy|etc. is
another dependency sysadmins need to configure and maintain in
addition to our daemons (and varnish).

Since our Perl code already needs to deal with IMAP scraper
bots, it might as well deal with HTTP(S) ones as well :P

Notable advantages of PublicInbox::PsgiRproxy over nginx for
HTTPS termination include:

* Response buffering is lazy by default.  That is, whereas (last
  I checked) nginx either (a) buffers a response body in full
  before sending it or (b) doesn't buffer at all.  (a) is safe
  for reducing backend contention but increases latency, whereas
  (b) allows slow clients to bottleneck fast backends.
  PublicInbox::PsgiRproxy defaults to writing to the client
  immediately (unbuffered), but starts buffering when a client
  can't keep up with the backend.

* Perl configuration allows easier development of custom modules
  ("Plack middleware" in our case) for inspection of requests in
  Perl.  It gives the PSGI env a way to detect reused connections
  and the $env->{'psgix.io'} handle can be used for TCP/TLS
  fingerprinting of suspected bots.

* Trailers following chunked requests are supported (but mapped to
  headers for the backend).  Response trailers are not yet
  supported since browsers (even bloated "modern" ones) can't
  seem to handle them.

This PSGI reverse proxy has been fronting https://public-inbox.org
and the https://yhbt.net/lore/ mirror for over a month, now.

2 months agohttp: expose reused connection counter in PSGI env
Eric Wong [Sun, 13 Jul 2025 00:08:34 +0000 (00:08 +0000)] 
http: expose reused connection counter in PSGI env

`$env->{pi-httpd.request_nr}' is now accessible via the PSGI
env for middlewares to use to identify reused connections.
This can be useful for middlewares to implement rules for
identifying and rejecting/allowing certain clients (e.g.
aggressive bots).

2 months agolistener: throttle errors for resource limits
Eric Wong [Sun, 13 Jul 2025 00:08:33 +0000 (00:08 +0000)] 
listener: throttle errors for resource limits

We don't want to flood a system and run the log partition
out of space if we hit EMFILE/ENFILE/ENOMEM/ENOBUFS

2 months agolei_mail_sync: fix size check for Maildir||MH files
Eric Wong [Tue, 8 Jul 2025 03:54:07 +0000 (03:54 +0000)] 
lei_mail_sync: fix size check for Maildir||MH files

`-s $fh' returns undef only when a file is non-existent and zero
when it's empty.  Thus so we must use `||' to skip empty files.
Furthermore, `-s FILEHANDLE' is never undef on open handles.

Fixes: 5aab49f3 (lei: support reading MH for convert+import+index, 2023-12-29)
2 months agonntpd|imapd|pop3d: wait on writability, first
Eric Wong [Wed, 2 Jul 2025 20:43:21 +0000 (20:43 +0000)] 
nntpd|imapd|pop3d: wait on writability, first

While instances of the first write(2) failing with EAGAIN
immediately after being returned by accept(2) is highly
unlikely, it still seems like a theoretical possibility.  So out
of an abundance of caution, we'll just wait on writability to
reduce branches in our code.

2 months agods: long_response: reduce {long_cb} size
Eric Wong [Sat, 28 Jun 2025 11:20:13 +0000 (11:20 +0000)] 
ds: long_response: reduce {long_cb} size

Invoking the `fileno' op should be trivial in cost and we can
reduce the amount of potentially long-lived storage for long
responses by invoking it with every long_step call.

2 months agoimap: fetch_compile: more meaningful variable names
Eric Wong [Sat, 28 Jun 2025 11:20:12 +0000 (11:20 +0000)] 
imap: fetch_compile: more meaningful variable names

Match the variable names used by callers to refuse confusion
rather than an ambiguously-named array and meaningless offsets.

2 months agods: long_response: fix bad comment
Eric Wong [Sat, 28 Jun 2025 11:20:11 +0000 (11:20 +0000)] 
ds: long_response: fix bad comment

As far as I can tell, {long} never existed as a field and
it's always meant the callback passed for ->long_response.

2 months agods: support enqueued CODE refs with arguments
Eric Wong [Sat, 28 Jun 2025 11:20:10 +0000 (11:20 +0000)] 
ds: support enqueued CODE refs with arguments

While we inherited support for enqueuing CODE refs from
Danga::Socket, it makes sense to support enqueuing CODE
refs with extra arguments specific to that CODE ref[1]
instead of relying on anonymous CODE refs to capture its
args..

Supporting args with CODE refs allows our ->flush_write
implementation to be more generic and no longer specific to
tmpio write buffering.  This change may eventually allow us
to chain multiple long_response calls together and eliminate
the {long_cb} field.

[1] we prefer CODE refs point to named subs with arguments
    passed to it to save RAM as opposed to constantly allocating
    new anonymous subs to capture local variables.

2 months agods: flush_write: check {sock} after calling CODE
Eric Wong [Mon, 23 Jun 2025 18:51:36 +0000 (18:51 +0000)] 
ds: flush_write: check {sock} after calling CODE

A user callback may call ->close and invalidate {sock} or even
replace it, so we must revalidate the {sock} field and break
break out of the loop ASAP to ensure we don't attempt further
operations on an invalid socket.

This really ought to fix the
`BUG: ep_mod GLOB=GLOB(...): no such file or directory' (ENOENT)
errors on EPOLL_CTL_MOD when a client shuts down TLS on us.

Followup-to: f7aaea70 (ds: shutdn_tls_step clobbers {wbuf} early, 2025-06-17)
2 months agoinbox: introduce lock_file for inbox_idle
Eric Wong [Fri, 20 Jun 2025 20:39:40 +0000 (20:39 +0000)] 
inbox: introduce lock_file for inbox_idle

We may support idling on inbox-ish objects such as
PublicInbox::ExtSearch or similar (ActivityPub) objects in the
future, so taking version-specific knowledge out of InboxIdle
make sense for future changes.

2 months agods: avoid ->method dispatch for `print'
Eric Wong [Tue, 17 Jun 2025 00:13:35 +0000 (00:13 +0000)] 
ds: avoid ->method dispatch for `print'

`->method' dispatches have always been slower than directly
invoking Perl ops without `->', so favor the latter.

2 months agods: shutdn_tls_step clobbers {wbuf} early
Eric Wong [Tue, 17 Jun 2025 00:13:34 +0000 (00:13 +0000)] 
ds: shutdn_tls_step clobbers {wbuf} early

Instead of prepending to {wbuf} and preserving tmpio objects
and/or other coderefs, clobber it early since we don't keep
sockets after shutting down TLS.  This ought to fix the `BUG:
ep_mod GLOB=GLOB(...): no such file or directory' (ENOENT)
errors on EPOLL_CTL_MOD when a client shuts down the connection
while serving large HTTPS responses.

2 months agotls: shorten warning $SSL_ERROR prefix
Eric Wong [Tue, 17 Jun 2025 00:13:33 +0000 (00:13 +0000)] 
tls: shorten warning $SSL_ERROR prefix

Use our usual `W:' warning prefix here since the stringified
form of the $SSL_ERROR magic variable will be something like:
"SSL read error error:xxx:SSL routines:SSL_shutdown:shutdown while in init"
and we don't need to be mentioning `SSL' ourselves.

2 months agods: confess on epoll_ctl failure
Eric Wong [Tue, 17 Jun 2025 00:13:32 +0000 (00:13 +0000)] 
ds: confess on epoll_ctl failure

ENOENT on epoll_ctl(2) is always a sign of buggy code and it
makes sense to dump the backtrace by default, here.  I've only
noticed this failure on HTTPS connections.

3 months agodaemon: introduce register_log public API
Eric Wong [Sat, 14 Jun 2025 09:26:35 +0000 (09:26 +0000)] 
daemon: introduce register_log public API

Having this public API would make it easier to configure
AccessLog and AccessLog::Timed middleware in multiple
areas while allowing SIGUSR1 to reopen them:

my $log_pfx = $ENV{LOGGER_PFX} // '/var/log/xyz/';
my $access_log = sub {
my ($pfx) = @_;
my $fh = PublicInbox::Daemon::register_log
$log_pfx.$pfx.'.access.log';
enable 'AccessLog::Timed',
logger => sub { syswrite $fh, $_[0] };
};
builder {
mount 'http://foo.example.com/' => builder {
$access_log->('foo');
$foo_app;
};
mount 'http://bar.example.com/' => builder {
$access_log->('bar');
$bar_app;
};
}

I'm not particularly happy about introducing a
public-inbox-specific API for .psgi file writers; I can't think
of another portable and reliable way to reopen all log files
used by a PSGI application upon SIGUSR1.

3 months agodaemon: reopen log files atomically
Eric Wong [Mon, 16 Jun 2025 20:05:13 +0000 (20:05 +0000)] 
daemon: reopen log files atomically

Ensuring there is no window for a Perl IO (file handle) to be
invalid is useful in case some code ever gets used in a
multithreaded environment.  The FD stability would also be less
confusing for users using `lsof(8)' or similar to track FDs.

3 months agodaemon: allow `out' directive for HTTP, too
Eric Wong [Sat, 14 Jun 2025 09:26:33 +0000 (09:26 +0000)] 
daemon: allow `out' directive for HTTP, too

While we currently don't output the the `out' directive for HTTP
as we do for protocols which lack PSGI support; the special case
for HTTP doesn't make sense as (in theory) a PSGI middleware
could someday exist to write to that destination.

3 months agohttp: response_write handles HEAD responses
Eric Wong [Sat, 14 Jun 2025 09:26:32 +0000 (09:26 +0000)] 
http: response_write handles HEAD responses

Since we special-case MSG_MORE for HEAD responses already,
our HTTP server code now strips response bodies from HEAD
requests unconditionally, allowing .psgi files to omit the
`enable "Head"' directive.

Proper placement of `Head' (Plack::Middleware::Head) in the
middleware stack can be confusing and tedious since it needs to
be after things like AccessLog::Timed which need to wrap the
response body with an object which responds to ->close.

Furthermore, removal of the response body in HEAD requests is
required in all relevant HTTP RFCs (2616, 7231 and 9110) and
doing HEAD response handling in the server itself avoids the
possibility of misconfiguration by the .psgi file maintainer.

The main reason in this rewrite was the desire to easily
configure per-map AccessLog::Timed destination log files.
Correct configuration without this patch would require putting
an `enable "Head"' directive inside every Plack::Builder::map
block after `enable "AccessLog::Timed"'.

In other words, the .psgi file having a single `enable "Head"'
encompassing everything:

builder {
enable 'Head';
map 'http://example.com/' => builder {
enable 'AccessLog::Timed', @a_params;
};
map 'http://example.net/' => builder {
enable 'AccessLog::Timed', @b_params;
};
};

...would not log HEAD requests properly, and the correct alternative:

builder {
map 'http://example.com/' => builder {
enable 'AccessLog::Timed', @a_params;
enable 'Head';
};
map 'http://example.net/' => {
enable 'AccessLog::Timed', @b_params;
enable 'Head';
};
};

...would be more tedious with many `map' directives.  With this
change, all `enable "Head"' directives can simply be omitted for
public-inbox-{netd,httpd} users.

3 months agotests: disable ContentLength middleware in *.psgi
Eric Wong [Sat, 14 Jun 2025 09:26:31 +0000 (09:26 +0000)] 
tests: disable ContentLength middleware in *.psgi

Our HTTP server code already calculates Content-Length in order to
support persistent connections, so the middleware is redundant.

3 months agowww_static: use Plack::Component->to_app
Eric Wong [Tue, 10 Jun 2025 05:18:10 +0000 (05:18 +0000)] 
www_static: use Plack::Component->to_app

Requiring users to create anonymous subs is tedious and
Plack::Component is already a part of Plack (and installed
by all PSGI users) and already used by many middlewares.

3 months agowww_static: support default_type parameter
Eric Wong [Tue, 10 Jun 2025 05:18:09 +0000 (05:18 +0000)] 
www_static: support default_type parameter

Sysadmins may wish to override the default
`application/octet-stream' for files with no suffix or unknown
suffixes.  One use case is serving the
https://public-inbox.org/README itself with the `text/plain'
Content-Type.

Plack::MIME->set_fallback can be an alternative to this
parameter, but its effect is interpreter-wide and server admins
may wish to set the default type on a per-route basis.

3 months agotreewide: consistent IO::Handle->flush error handling
Eric Wong [Sat, 7 Jun 2025 19:48:37 +0000 (19:48 +0000)] 
treewide: consistent IO::Handle->flush error handling

We'll attempt to stringify the failing IO handle in case
Perl can output more info about the object.  Furthermore,
we can disable checks for `print' failures preceding ->flush
since they're redundant and a waste of code.

3 months agolei_saved_search: show errno on ->flush failure
Eric Wong [Sat, 7 Jun 2025 19:48:36 +0000 (19:48 +0000)] 
lei_saved_search: show errno on ->flush failure

->flush failures need to describe the nature of the
failure, not just the failing handle.

3 months agotest_common: fix xap_helper build reuse
Eric Wong [Fri, 6 Jun 2025 10:24:27 +0000 (10:24 +0000)] 
test_common: fix xap_helper build reuse

The change in commit c45a5498 to avoid copying introduced a
major test performance regression since the extra XFLAGS and
xap_modversion files (required to detect changes for rebuilding)
weren't copied.  Instead, just symlink the entire
architecture-specific directory instead of individual files.

I didn't notice the slowdown earlier since I only tested on a
busy system where my worktree is on an overloaded HDD.

Fixes: c45a5498 (test_common: symlink xap_helper instead of excessive copying, 2025-06-01)
3 months agohttp: don't ->close after ->accept_SSL failure
Eric Wong [Thu, 5 Jun 2025 06:53:36 +0000 (06:53 +0000)] 
http: don't ->close after ->accept_SSL failure

->accept_SSL failures leaves the socket ref as a GLOB (not
IO::Handle) and unable to respond to the ->close method.
Calling close in any form isn't actually necessary at all,
so just let refcounting destroy the socket.

Followup-to: e85d3280 (ds: don't try ->close after ->accept_SSL failure, 2023-11-02)
3 months agohttp: respect `Connection: close' in HTTP/1.1 requests
Eric Wong [Wed, 4 Jun 2025 11:34:58 +0000 (11:34 +0000)] 
http: respect `Connection: close' in HTTP/1.1 requests

HTTP/1.1 clients may wish to explicitly terminate the connection
upon reading the response.  While clients may specify HTTP/1.0
to force a disconnect, HTTP/1.0 lacked chunked encoding which
meant streaming responses of indeterminate length could be
silently truncated at the protocol level and undetectable in
cases of uncompressed plain-text.

3 months agods: do_read: return 0 on EOF
Eric Wong [Wed, 4 Jun 2025 11:34:57 +0000 (11:34 +0000)] 
ds: do_read: return 0 on EOF

It's conceivable that future users of this internal function
will want to distinguish between a client which failed due to
some network or HW error or a normal disconnect.

3 months agofix git permission denied in certain configs
James Bottomley [Thu, 29 May 2025 15:23:21 +0000 (11:23 -0400)] 
fix git permission denied in certain configs

The problem is the which() sub in Spawn.pm only checks if the path
fragment is executable, but this may be true of a directory as well.

In particular, a lot of people have a git directory in their $HOME
(where they store their git repos) and if $PATH contains '.' then
SpawnPP.pm will try to execute this directory leading to this failure
on a lot of lei commands:

exec ./git var GIT_PAGER failed: Permission denied at /usr/share/perl5/PublicInbox/SpawnPP.pm line 62.
fork_exec git var GIT_PAGER failed: Permission denied

As you can see what's happened is it's tried to execute my ./git
directory, which obviously fails.  The fix is simple, skip executable
files which are also directories when doing $PATH based lookup

[ew: use `_' bareword to avoid 2nd stat, spelling fix in commit message]

Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
3 months agoextmsg: improve handling of obfuscated Message-ID URLs
Eric Wong [Thu, 29 May 2025 00:20:31 +0000 (00:20 +0000)] 
extmsg: improve handling of obfuscated Message-ID URLs

Some email clients (or 3rd-party archival sites) apparently
mistake Message-IDs as email addresses and replace the last part
of the unique (non-domain) prefix before the `@' with `...'.
The `...' causes Xapian query parser to attempt to interpret
the Message-ID portion as a range.  So attempt to replace
eliminated portions (`...') with the wildcard operator (`*')
and hope it doesn't overload Xapian.

We'll also use wildcards more liberally for long word-like
sequences to improve success rate and improve diagnostics for
failed queries while we're at it.

3 months agoxap_helper: mset: ignore non-ultimate exceptions
Eric Wong [Thu, 29 May 2025 00:20:30 +0000 (00:20 +0000)] 
xap_helper: mset: ignore non-ultimate exceptions

For requests consisting of multiple queries, ignore Xapian
exceptions for all but the last query.  When sending multiple
queries to the `mset' xap_helper command, we only care about the
result of the first successful query where success is defined as
a non-empty mset response.  Thus an exception is considered an
empty mset and we can try other queries from the same request.

3 months agoxhc_mset: improve error message formatting
Eric Wong [Thu, 29 May 2025 00:20:29 +0000 (00:20 +0000)] 
xhc_mset: improve error message formatting

We don't need extra linefeeds before the `)' in the stderr
output, and the context from croak() is bogus anyways since we
get error messages from an external process.

3 months agotest_common: symlink xap_helper instead of excessive copying
Eric Wong [Sun, 1 Jun 2025 10:14:47 +0000 (10:14 +0000)] 
test_common: symlink xap_helper instead of excessive copying

Copying the entire directory is racy in the face of parallel
tests and caused an occasional test failure due to attempts (and
failures) in copying short-lived temporary files.  A symlink
for just the executable avoids attempted copies temporary files
is less expensive for the intended file, as well.

4 months agowww: extmsg: dedupe cross-posted Message-IDs
Eric Wong [Mon, 12 May 2025 20:45:01 +0000 (20:45 +0000)] 
www: extmsg: dedupe cross-posted Message-IDs

Having redundant links to the same cross-posted message is
unlikely to be of help, especially when using an /all/ extindex.
So just grab the first matching inbox + Message-ID combo and
ignore subsequent URLs for Message-IDs which exist in multiple
inboxes/extindices.

4 months agowww: extmsg: async partial Message-ID search
Eric Wong [Mon, 12 May 2025 20:45:00 +0000 (20:45 +0000)] 
www: extmsg: async partial Message-ID search

While partial Message-ID matches tend to be relatively fast,
there's still some potential for pathological matches taking
a long time and blocking our event loop.  So throw them over
to the external xap_helper process to ensure our main event
loop can handle other HTTP/NNTP/POP3/IMAP requests while
potentially waiting on Xapian disk seeks.

4 months agoxap_helper: mset supports multiple requests
Eric Wong [Mon, 12 May 2025 20:44:59 +0000 (20:44 +0000)] 
xap_helper: mset supports multiple requests

By supporting multiple queries in one IPC call, we can reduce
IPC traffic for search endpoints which make multiple search
requests but only use the result of the first.  This will be
used for partial Message-ID matching for handling truncated
URLs.

4 months agowww: extmsg: rely on event loop for partial matches
Eric Wong [Mon, 12 May 2025 20:44:58 +0000 (20:44 +0000)] 
www: extmsg: rely on event loop for partial matches

Preparation for (optionally) using xap_helper to asynchronously
look up Message-ID matches.  This change does not materially
change behavior, but will make subsequent changes easier to
digest.

4 months agosha: avoid string eval for loading Net::SSLeay
Eric Wong [Fri, 9 May 2025 23:11:57 +0000 (23:11 +0000)] 
sha: avoid string eval for loading Net::SSLeay

We can rely on require + import and use glob assigments to give
anonymous subs a place in the symbol table.  This ought to make
it easier to do compile-only syntax checking and hopefully
easier for a hypothetical Perl implementation to optimize.

4 months agorename PlackLimiter to PsgiLimiter
Eric Wong [Wed, 7 May 2025 00:16:34 +0000 (00:16 +0000)] 
rename PlackLimiter to PsgiLimiter

`PSGI' is probably a appropriate name since it's the name
of the interface, and `Plack' is merely the implementation
of it.

4 months agohttpd: psgix.io is the underlying IO object
Eric Wong [Wed, 7 May 2025 00:16:33 +0000 (00:16 +0000)] 
httpd: psgix.io is the underlying IO object

PSGI::Extensions(3pm) states psgix.io should be "the raw IO
socket", so we shall conform to that specification.  For our
codebase to continue taking advantage of
public-inbox-daemon-specific features, we now expose the
PublicInbox::HTTP object via $env->{'pi-httpd.client'}.  The
`pi-httpd.async' boolean field is now gone, since we can just
test for the presence of `pi-httpd.app' or `pi-httpd.client'
to avoid occupying another hash table entry.

4 months agosyscall: fix fstatfs(2) use for x32
Eric Wong [Thu, 1 May 2025 23:18:10 +0000 (23:18 +0000)] 
syscall: fix fstatfs(2) use for x32

x32 syscalls all seem to have 0x40000000 added to them, so the
low x86-64 number seems obviously wrong.  Tested on an x32 btrfs
chroot where I also realized the statfs.f_type field is 8 bytes
since x32 kernels are 64-bit.

4 months agot/netd: skip Linux-only test in some cases
Eric Wong [Fri, 2 May 2025 11:11:11 +0000 (11:11 +0000)] 
t/netd: skip Linux-only test in some cases

Apparently, some ss(8) on some older Linux kernels don't seem to
report Send-Q properly for Unix stream socket listeners.

4 months agosolver: accept extra trailing space in filenames of diffs
Eric Wong [Thu, 1 May 2025 19:35:55 +0000 (19:35 +0000)] 
solver: accept extra trailing space in filenames of diffs

Some crazy MUAs add trailing whitespace which throws off our
strict patch parsing.  So fall back to stripping trailing
whitespace and retrying to match the filename from `git
ls-files'.  Stripping trailing whitespace matches our liberal
acceptance of trailing whitespace when using `git apply', too.

For debug messages, switch to `B::cstring' for unconditionally
quoting pathnames since our `git_quote' sub matches the
problematic git(1) behavior in not quoting actual pathnames with
trailing whitespace.  `B' is the Perl compiler backend
module, which (AFAIK) is in every Perl 5 installation,
so its use shouldn't cause burdens to our existing users.

c.f. <AM9PR04MB860415AC2AE7973F9F0A4AA095E72@AM9PR04MB8604.eurprd04.prod.outlook.com>

4 months agospawn: avoid string eval for optional BSD::Resource
Eric Wong [Tue, 29 Apr 2025 20:47:46 +0000 (20:47 +0000)] 
spawn: avoid string eval for optional BSD::Resource

BSD::Resource has had a get_rlimits() sub for a while to return
a hashref to non-Inline::C users.  So favor using that after
`require' rather than a string eval which can't be checked for
syntax-only errors.