The jail spawn path launches its wrapper by bare name (sudo / jexec / mdo)
and relies on execvp + the daemon's inherited PATH. Under daemon(8)/rc the
PATH is often empty or reordered, so execvp either misses the binary (ENOENT)
or hits a non-executable same-named entry first and returns EACCES — the spawn
"Permission denied" seen on FreeBSD even though the identical command runs from
a shell.
- resolve_program() absolutizes a bare program name against a fixed search
list (first regular executable wins), leaving slash-bearing paths untouched
and falling back to the bare name so the OS still reports a real error.
- spawn_prepared_child now logs the resolved program, requested name, full
argv, and PATH before spawning. The previous "attempting spawn" log carried
no spawn-context detail, which is why the failure was opaque.
This removes the PATH-search EACCES as a variable so a truss/ktrace run can
attribute any remaining denial to an actual kernel/MAC policy instead.
Tests: resolve_program pass-through, absolutization, and missing-name fallback.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>