fix(ssh): keep lsof holder evidence usable on hosts with unstat-able mounts

lsof warns to stderr about mounts it cannot stat, and any stderr byte forced
the holder enumeration to 'unavailable' — making the 'exited' verdict, and so
husk reaping, unreachable on those hosts. Pass -w to suppress the warnings.
This commit is contained in:
Neil
2026-09-13 19:20:04 -07:00
parent bfd53b9f8f
commit 10d7b0db37
3 changed files with 26 additions and 4 deletions
@@ -181,7 +181,7 @@ describe('probeRelayEndpointIncumbent', () => {
describe('relayEndpointIncumbentProbeCommand', () => {
it('ANDs the lsof selectors so it cannot match unrelated unix-socket holders', () => {
expect(RELAY_LSOF_PROBE_JS).toContain("['-t', '-a', '-U', process.argv[1]]")
expect(RELAY_LSOF_PROBE_JS).toContain("['-w', '-t', '-a', '-U', process.argv[1]]")
})
it('never unlinks the relay endpoint', () => {
@@ -44,7 +44,12 @@ process.kill = function(pid, signal) {
};
`
async function runProbe(options: { signal?: string; census?: string; censusKillDenied?: boolean }) {
async function runProbe(options: {
signal?: string
census?: string
censusKillDenied?: boolean
lsof?: string
}) {
const dir = mkdtempSync(join(tmpdir(), 'orca-lsof-lifecycle-'))
const pidFile = join(dir, 'lsof.pid')
const psPidFile = join(dir, 'ps.pid')
@@ -52,7 +57,7 @@ async function runProbe(options: { signal?: string; census?: string; censusKillD
writeFileSync(join(dir, 'preload.cjs'), PRELOAD)
writeFileSync(
join(dir, 'lsof'),
`#!/bin/sh\necho 123\n${options.signal ? 'exec sleep 60\n' : 'exit 2\n'}`,
options.lsof ?? `#!/bin/sh\necho 123\n${options.signal ? 'exec sleep 60\n' : 'exit 2\n'}`,
{ mode: 0o755 }
)
if (options.census !== undefined) {
@@ -111,6 +116,23 @@ async function runProbe(options: { signal?: string; census?: string; censusKillD
}
describe.skipIf(process.platform === 'win32')('lsof supervisor lifecycle', () => {
it('keeps holder evidence usable when lsof would warn about an unstat-able mount', async () => {
const result = await runProbe({
lsof: [
'#!/bin/sh',
'quiet=',
'for arg in "$@"; do [ "$arg" = "-w" ] && quiet=1; done',
'[ -z "$quiet" ] && echo "lsof: WARNING: can\'t stat() nfs fs /mnt/stale" >&2',
'echo 123',
'exit 0'
].join('\n')
})
expect(result).toMatchObject({ code: 0, timedOut: false })
const [marker, ...pids] = result.stdout.split('\n')
expect(marker).toBe('lsof')
expect(pids.filter(Boolean)).toEqual(['123'])
})
it.each(['SIGTERM', 'SIGHUP', 'SIGINT'])(
'owns lsof when %s arrives inside spawn',
async (signal) => {
+1 -1
View File
@@ -112,7 +112,7 @@ function cleanup() {
['SIGTERM', 'SIGHUP', 'SIGINT'].forEach(function(signal) {
process.on(signal, function() { unavailable = true; cleanup(); });
});
child = spawn('lsof', ['-t', '-a', '-U', process.argv[1]], {
child = spawn('lsof', ['-w', '-t', '-a', '-U', process.argv[1]], {
detached: true,
stdio: ['ignore', 'pipe', 'pipe']
});