Candidate527b9a1e6(gander/row196-r6-prd19, basef48725cfc), the 39 manifest paths of CANDIDATE.sha256 537d30aef70b6cd1 atec1e7fad8. Duckworth approved round 1 outside B1/B2 (rev 2973, comment 30145) and round 2 merge-scoped (rev 2986, comment 30156). Gate gander-0066 on grant sage-2145: 2334 pass, 0 fail, 1 skip; mutants 38/38 killed. BUILD-LOG lines and Duckworth's C1 DEFERRED entry follow as a doc-only append.
59 lines
2.5 KiB
JavaScript
59 lines
2.5 KiB
JavaScript
import { spawn as nodeSpawn } from 'node:child_process';
|
|
import { fileURLToPath } from 'node:url';
|
|
import { lstatSync } from 'node:fs';
|
|
import { BusError } from './broker.mjs';
|
|
|
|
const helper = fileURLToPath(new URL('./peercred.py', import.meta.url));
|
|
const refused = () => new BusError('peercred-required');
|
|
export function createPeerReader({ interpreter, spawn = nodeSpawn, timeout = 1000, stat = lstatSync } = {}) {
|
|
try { const s = stat(helper); if (!s.isFile() || s.isSymbolicLink()) throw Error(); }
|
|
catch { throw refused(); }
|
|
const children = new Set();
|
|
let closed = false;
|
|
return {
|
|
read(socket) {
|
|
if (closed || children.size >= 16) return Promise.reject(refused());
|
|
return new Promise((resolve, reject) => {
|
|
let child, timer, output = '', settled = false, invalid = false;
|
|
const finish = (error, value) => {
|
|
if (settled) return;
|
|
settled = true;
|
|
clearTimeout(timer);
|
|
error ? reject(refused()) : resolve(value);
|
|
};
|
|
try {
|
|
// Keep this connection's Socket in this closure. Never borrow another pending fd.
|
|
child = spawn(interpreter.path, ['-I', '-S', helper],
|
|
{ env: {}, stdio: ['ignore', 'pipe', 'pipe', socket] });
|
|
children.add(child);
|
|
timer = setTimeout(() => { child.kill('SIGKILL'); finish(true); }, timeout);
|
|
child.stdout.on('data', data => {
|
|
output += data.toString('utf8');
|
|
if (Buffer.byteLength(output) > 256) { invalid = true; child.kill('SIGKILL'); finish(true); }
|
|
});
|
|
child.stderr.on('data', () => { invalid = true; });
|
|
child.on('error', () => { children.delete(child); finish(true); });
|
|
child.on('close', (code, signal) => {
|
|
children.delete(child);
|
|
if (settled) return;
|
|
try {
|
|
if (closed || invalid || code !== 0 || signal) throw Error();
|
|
const p = JSON.parse(output);
|
|
if (!p || Array.isArray(p) || Object.keys(p).sort().join(',') !== 'gid,pid,uid' ||
|
|
!['pid', 'uid', 'gid'].every(k => Number.isSafeInteger(p[k])) || p.pid < 2 || p.uid < 0 || p.gid < 0) throw Error();
|
|
finish(false, Object.freeze(p));
|
|
} catch { finish(true); }
|
|
});
|
|
} catch { finish(true); }
|
|
});
|
|
},
|
|
async close() {
|
|
closed = true;
|
|
await Promise.all([...children].map(child => new Promise(resolve => {
|
|
child.once('close', resolve);
|
|
child.kill('SIGKILL');
|
|
})));
|
|
},
|
|
};
|
|
}
|