2 Commits
Author SHA1 Message Date
Thomas VincentandTheWitness 90da94b94f fix(process): unify subprocess ownership and child lifecycle safety (#614)
## Summary

- make subprocess-registry detach atomic so exactly one closer owns each
entry
- make cancellation cleanup release only the detached node it owns
- replace `vfork`/`execve` child setup with `posix_spawn` and explicit
file actions
- mark every pipe and stdio-collision duplicate close-on-exec
- restore the child's default SIGPIPE disposition while preserving the
parent's handling
- bound TERM/KILL reaping and park unreaped children in a fixed-capacity
sweep list
- harden PHP script-server slot ownership, startup, restart, signal,
descriptor, and cleanup paths
- retire and restart failed or oversized reads so stale response tails
cannot poison later data sources
- revalidate every command retry and cap recovery handshakes at two
seconds

This folds the child-process lifecycle portion of #597 into the narrower
ownership PR. It does not absorb unrelated poll-host, SQL, SNMP, ICMP,
or build-infrastructure work.

## Review-driven corrections

- close-on-exec is explicitly restored after `dup()` in both read and
write stdio-collision branches
- the source invariant check now uses portable POSIX character classes
- direct tests cover duplicate flags, both collision branches, failure
after pipe creation, abandoned-child sweeping, and capacity overflow
- collision cleanup preserves current-child redirects even when an older
registry entry occupies fd 0 or 1
- script children receive SIGTERM before SIGKILL and unreaped PHP
children transfer to the bounded sweep registry
- the Docker integration suite now starts a real PHP script server,
verifies its startup protocol and database result, and checks that
shutdown leaves no zombie PHP process

## Validation

- Ubuntu 24.04 container with GCC and GNU ld: `make check`, 6/6 targets
passed with no skips, including `test_php_runtime`
- real PHP script-server Docker integration: 6/6 checks passed; exact
value `42` reached `poller_output` and no zombie remained
- macOS Homebrew dependency build passed
- macOS `make check`: 5/5 available binaries passed; GNU `--wrap`
runtime suite is Linux-only
- PHP fixture syntax checked through mise PHP 8.3
- actionlint, shellcheck, and `git diff --check` passed

Closes #610.

---------

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Co-authored-by: TheWitness <thewitness@cacti.net>
2026-09-22 17:24:04 -04:00
Thomas Vincent d63e136b89 test: add the unit, fuzz and integration suites (#563)
Split out of #542. Tests and CI only, no source file is touched, so this
cannot conflict with anything currently open.

Verified in a clean `ubuntu:24.04` container against develop as it
stands.

**Unit** — 39 cmocka assertions across three binaries, run with `make -C
tests/unit`:

```
tests: 16 test(s) run.  PASSED
tests: 20 test(s) run.  PASSED
tests: 3 test(s) run.   PASSED
```

`test_safety_fixes.c` covers the allocation, bounds and reap paths;
`test_build_fixes.c` covers `regex_replace` and the config defaults;
`test_util_strings.c` covers `strpos`.

**Fuzz** — a `get_namebyhost` target with a 26-input corpus, built with
`-fsanitize=fuzzer,address,undefined`:

```
#2000	DONE   cov: 64 ft: 85 corp: 11/48b
```

**Integration** — the SNMPv3 fixture polls a live `snmpd` container and
asserts on the rows spine wrote to `poller_output` rather than on its
exit code, plus scripted checks for CLI argument handling, the script
server reap, and `output_regex` column detection.

**Not included.** The `fuzz_icmp_reply` target stays on #542, because it
calls `spine_icmp_classify_reply()`, which that PR adds. Everything else
stands on its own here.

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
2026-08-31 10:05:34 +01:00