Orginize
This commit is contained in:
@@ -0,0 +1,569 @@
|
||||
/**
|
||||
* Logical process table for `/proc`-style introspection (guest has no real PIDs).
|
||||
*/
|
||||
|
||||
/** Stable synthetic initd pid mapping for the current booter session. */
|
||||
const INITD_SYNTH_PID_BASE = 5100
|
||||
/** @type {Map<string, number>} */
|
||||
const initdSyntheticPidByName = new Map()
|
||||
let nextInitdSyntheticPid = INITD_SYNTH_PID_BASE
|
||||
/** @type {Map<number, { createdAtMs: number, lastStateChangeAtMs: number, lastState: string, completedAtMs?: number }>} */
|
||||
const shellJobLifecycleById = new Map()
|
||||
/** @type {Map<string, { createdAtMs: number, lastStateChangeAtMs: number, lastState: string, completedAtMs?: number }>} */
|
||||
const initdLifecycleByName = new Map()
|
||||
|
||||
/**
|
||||
* @param {string} unitName
|
||||
*/
|
||||
function syntheticInitdPidFor(unitName) {
|
||||
const k = String(unitName || '').trim()
|
||||
if (!k) return nextInitdSyntheticPid++
|
||||
const existing = initdSyntheticPidByName.get(k)
|
||||
if (Number.isFinite(existing)) return existing
|
||||
const pid = nextInitdSyntheticPid++
|
||||
initdSyntheticPidByName.set(k, pid)
|
||||
return pid
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} id
|
||||
* @param {string} state
|
||||
* @param {number} now
|
||||
*/
|
||||
function observeShellJobLifecycle(id, state, now) {
|
||||
const cur = shellJobLifecycleById.get(id)
|
||||
if (!cur) {
|
||||
const row = { createdAtMs: now, lastStateChangeAtMs: now, lastState: state }
|
||||
if (state === 'zombie') row.completedAtMs = now
|
||||
shellJobLifecycleById.set(id, row)
|
||||
return row
|
||||
}
|
||||
if (cur.lastState !== state) {
|
||||
cur.lastState = state
|
||||
cur.lastStateChangeAtMs = now
|
||||
if (state === 'zombie' && !Number.isFinite(cur.completedAtMs)) cur.completedAtMs = now
|
||||
}
|
||||
return cur
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} unit
|
||||
* @param {string} state
|
||||
* @param {number} startedAtMs
|
||||
* @param {number} now
|
||||
*/
|
||||
function observeInitdLifecycle(unit, state, startedAtMs, now) {
|
||||
const cur = initdLifecycleByName.get(unit)
|
||||
if (!cur) {
|
||||
const row = {
|
||||
createdAtMs: Number.isFinite(startedAtMs) ? startedAtMs : now,
|
||||
lastStateChangeAtMs: now,
|
||||
lastState: state
|
||||
}
|
||||
if (state === 'failed' || state === 'dead' || state === 'inactive') row.completedAtMs = now
|
||||
initdLifecycleByName.set(unit, row)
|
||||
return row
|
||||
}
|
||||
if (cur.lastState !== state) {
|
||||
cur.lastState = state
|
||||
cur.lastStateChangeAtMs = now
|
||||
if (
|
||||
(state === 'failed' || state === 'dead' || state === 'inactive') &&
|
||||
!Number.isFinite(cur.completedAtMs)
|
||||
) {
|
||||
cur.completedAtMs = now
|
||||
}
|
||||
if (state === 'active' || state === 'starting') cur.completedAtMs = undefined
|
||||
}
|
||||
return cur
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Record<string, string | undefined> | null | undefined} env
|
||||
*/
|
||||
function subprocessBridgeMetaFromEnv(env) {
|
||||
if (!env || typeof env !== 'object') return null
|
||||
const raw = String(env.BARE_OS_SUBPROCESS_BRIDGE_META_JSON || '')
|
||||
.trim()
|
||||
.slice(0, 8192)
|
||||
if (!raw) return null
|
||||
try {
|
||||
const o = JSON.parse(raw)
|
||||
if (!o || typeof o !== 'object' || Array.isArray(o)) return null
|
||||
const schema =
|
||||
typeof o.schema === 'number'
|
||||
? o.schema
|
||||
: typeof o.schemaVersion === 'number'
|
||||
? o.schemaVersion
|
||||
: null
|
||||
const hostPidMap =
|
||||
o.hostPidMap && typeof o.hostPidMap === 'object' && !Array.isArray(o.hostPidMap)
|
||||
? o.hostPidMap
|
||||
: null
|
||||
return {
|
||||
schema,
|
||||
hostPidMapEntryCount: hostPidMap ? Object.keys(hostPidMap).length : null,
|
||||
note: 'Non-secret summary of BARE_OS_SUBPROCESS_BRIDGE_META_JSON for bare-process / bare-subprocess alignment.'
|
||||
}
|
||||
} catch {
|
||||
return { parseError: true }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{
|
||||
* sessionId?: string,
|
||||
* bootStartedMs?: number,
|
||||
* signalState?: Map<number, { signal: string, atMs: number }>,
|
||||
* shellJobs?: Array<{ id?: number, done?: boolean, stopped?: boolean, pgid?: number, sid?: number, label?: string }>,
|
||||
* ipcStats?: { processGroups?: Record<string, unknown> } | null,
|
||||
* logicalFdRows?: Array<{ fd: number, target: string }>,
|
||||
* initdReadiness?: { units?: Array<{ name: string, phase: string, startedAtMs: number, error?: string }>, schema?: number } | null,
|
||||
* sigactionHandlers?: Record<string, string> | null,
|
||||
* env?: Record<string, string | undefined> | null,
|
||||
* niceByPid?: Map<number, number> | null
|
||||
* }} opts
|
||||
*/
|
||||
function bareOsClampNice(n) {
|
||||
if (!Number.isFinite(n)) return 0
|
||||
return Math.max(-20, Math.min(19, Math.trunc(n)))
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} pid
|
||||
* @param {Map<number, number> | null | undefined} niceByPid
|
||||
*/
|
||||
function niceForPid(pid, niceByPid) {
|
||||
if (!(niceByPid instanceof Map)) return 0
|
||||
const v = niceByPid.get(pid)
|
||||
return bareOsClampNice(typeof v === 'number' ? v : 0)
|
||||
}
|
||||
|
||||
/**
|
||||
* Logical `/proc/bare_os/process_maps.json` from a process table snapshot (not host VMAs).
|
||||
* @param {ReturnType<typeof bareOsProcessTableSnapshot>} snap
|
||||
*/
|
||||
export function bareOsBuildLogicalMemoryMapsProc(snap) {
|
||||
const processes = Array.isArray(snap.processes) ? snap.processes : []
|
||||
/** @type {Record<string, unknown>[]} */
|
||||
const regions = []
|
||||
for (const p of processes) {
|
||||
const pid = p && typeof p.pid === 'number' ? p.pid : NaN
|
||||
if (!Number.isFinite(pid)) continue
|
||||
const base = 0x40000000 + (pid | 0) * 0x00100000
|
||||
const toHex = (x) => '0x' + (x >>> 0).toString(16)
|
||||
regions.push({
|
||||
pid,
|
||||
start: toHex(base),
|
||||
end: toHex(base + 0x80000),
|
||||
perms: 'r--p',
|
||||
kind: 'guest-text',
|
||||
pathname: '/boot/init.js (logical guest image)'
|
||||
})
|
||||
regions.push({
|
||||
pid,
|
||||
start: toHex(base + 0x100000),
|
||||
end: toHex(base + 0x300000),
|
||||
perms: 'rw-p',
|
||||
kind: 'guest-data',
|
||||
pathname: '(anonymous logical)'
|
||||
})
|
||||
}
|
||||
return {
|
||||
schema: 2,
|
||||
model: 'bare-os-logical-v1',
|
||||
processTableSchemaRef:
|
||||
typeof snap.schemaVersion === 'number'
|
||||
? snap.schemaVersion
|
||||
: typeof snap.schema === 'number'
|
||||
? snap.schema
|
||||
: null,
|
||||
atMs: Date.now(),
|
||||
note: 'Logical memory regions per synthetic PID for dashboards; not Linux /proc/pid/maps and not host VMAs.',
|
||||
regions
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ReturnType<typeof bareOsProcessTableSnapshot>} snap
|
||||
* @param {unknown} bareLib ctx.bare when available
|
||||
*/
|
||||
export function bareOsBuildProcessThreadsProc(snap, bareLib) {
|
||||
const processes = Array.isArray(snap.processes) ? snap.processes : []
|
||||
/** @type {Record<string, unknown>[]} */
|
||||
const byPid = []
|
||||
let bareThreadPresent = false
|
||||
try {
|
||||
const bt =
|
||||
bareLib &&
|
||||
typeof bareLib === 'object' &&
|
||||
/** @type {{ bareThread?: unknown }} */ (bareLib).bareThread
|
||||
bareThreadPresent = !!(bt && typeof bt === 'object')
|
||||
} catch {
|
||||
bareThreadPresent = false
|
||||
}
|
||||
for (const p of processes) {
|
||||
const pid = p && typeof p.pid === 'number' ? p.pid : NaN
|
||||
if (!Number.isFinite(pid)) continue
|
||||
/** @type {Record<string, unknown>[]} */
|
||||
const threads = [
|
||||
{
|
||||
tid: pid,
|
||||
name: 'main',
|
||||
state: String((p && p.state) || 'unknown'),
|
||||
accountingSource: 'logical-main'
|
||||
}
|
||||
]
|
||||
if (bareThreadPresent && pid === 3) {
|
||||
threads.push({
|
||||
tid: pid + 1_000_000,
|
||||
name: 'bare-runtime',
|
||||
state: 'attached',
|
||||
accountingSource: 'ctx.bare.bareThread'
|
||||
})
|
||||
}
|
||||
byPid.push({ pid, threads })
|
||||
}
|
||||
return {
|
||||
schema: 2,
|
||||
model: 'bare-os-logical-v1',
|
||||
processTableSchemaRef:
|
||||
typeof snap.schemaVersion === 'number'
|
||||
? snap.schemaVersion
|
||||
: typeof snap.schema === 'number'
|
||||
? snap.schema
|
||||
: null,
|
||||
atMs: Date.now(),
|
||||
note: 'One logical main thread per synthetic PID; optional bare-runtime row on PID 3 when ctx.bare exposes bareThread.',
|
||||
bareThreadModulePresent: bareThreadPresent,
|
||||
byPid
|
||||
}
|
||||
}
|
||||
|
||||
export function bareOsProcessTableSnapshot(opts = {}) {
|
||||
const sid = String(opts.sessionId || '').trim().slice(0, 128)
|
||||
const boot = typeof opts.bootStartedMs === 'number' ? opts.bootStartedMs : 0
|
||||
const now = Date.now()
|
||||
const sigState = opts.signalState instanceof Map ? opts.signalState : new Map()
|
||||
const logicalRows = Array.isArray(opts.logicalFdRows) ? opts.logicalFdRows : []
|
||||
const virtualSignalDeliveries = []
|
||||
for (const [pid, v] of sigState.entries()) {
|
||||
virtualSignalDeliveries.push({ pid, signal: v.signal, atMs: v.atMs })
|
||||
}
|
||||
const accountingOn =
|
||||
opts.env &&
|
||||
(opts.env.BARE_OS_PROCESS_ACCOUNTING === '1' ||
|
||||
opts.env.BARE_OS_PROCESS_ACCOUNTING === 'true')
|
||||
const accountingSource = accountingOn
|
||||
? 'guest-accounting-env'
|
||||
: 'synthetic-default'
|
||||
const niceByPid = opts.niceByPid
|
||||
const v8m = (pid) => ({
|
||||
threads: 1,
|
||||
cpuMsTotal: 0,
|
||||
cpuMsWindow: 0,
|
||||
readBytes: 0,
|
||||
writeBytes: 0,
|
||||
syscallsDelta: 0,
|
||||
memRssBytes: null,
|
||||
wchan: '',
|
||||
ioPrio: null,
|
||||
accountingSource,
|
||||
nice: niceForPid(pid, niceByPid)
|
||||
})
|
||||
const mk = (pid, ppid, name, startedAtMs, pgid, sessionId) => {
|
||||
const sig = sigState.get(pid)
|
||||
const pg = typeof pgid === 'number' ? pgid : pid
|
||||
const sidN = typeof sessionId === 'number' ? sessionId : 1
|
||||
return {
|
||||
pid,
|
||||
ppid,
|
||||
pgid: pg,
|
||||
sid: sidN,
|
||||
name,
|
||||
state: sig ? 'signaled' : 'running',
|
||||
startedAtMs,
|
||||
lastSignal: sig ? sig.signal : undefined,
|
||||
lastSignalAtMs: sig ? sig.atMs : undefined,
|
||||
...v8m(pid)
|
||||
}
|
||||
}
|
||||
const jobs = Array.isArray(opts.shellJobs) ? opts.shellJobs : []
|
||||
const jobProcs = jobs
|
||||
.filter((j) => j && !j.done && typeof j.id === 'number')
|
||||
.map((j) => {
|
||||
const pid = 4100 + j.id
|
||||
const pgid = typeof j.pgid === 'number' ? j.pgid : pid
|
||||
const session = typeof j.sid === 'number' ? j.sid : 1
|
||||
const life = observeShellJobLifecycle(j.id, j.stopped ? 'stopped' : 'running', now)
|
||||
return {
|
||||
pid,
|
||||
ppid: 3,
|
||||
pgid,
|
||||
sid: session,
|
||||
name: 'bare-os-shell-job',
|
||||
state: j.stopped ? 'stopped' : 'running',
|
||||
startedAtMs: life.createdAtMs,
|
||||
createdAtMs: life.createdAtMs,
|
||||
lastStateChangeAtMs: life.lastStateChangeAtMs,
|
||||
completedAtMs: life.completedAtMs,
|
||||
jobId: j.id,
|
||||
role: 'shell_job',
|
||||
lifecyclePhase: j.stopped ? 'suspended' : 'running',
|
||||
label: typeof j.label === 'string' ? j.label.slice(0, 256) : undefined,
|
||||
stopped: !!j.stopped,
|
||||
...v8m(pid)
|
||||
}
|
||||
})
|
||||
const zombieJobs = jobs.filter((j) => j && j.done === true && typeof j.id === 'number')
|
||||
const zombieProcs = zombieJobs.map((j) => {
|
||||
const jid = typeof j.id === 'number' ? j.id : 0
|
||||
const pid = 4200 + jid
|
||||
const pgid = typeof j.pgid === 'number' ? j.pgid : pid
|
||||
const session = typeof j.sid === 'number' ? j.sid : 1
|
||||
const life = observeShellJobLifecycle(jid, 'zombie', now)
|
||||
return {
|
||||
pid,
|
||||
ppid: 3,
|
||||
pgid,
|
||||
sid: session,
|
||||
name: 'bare-os-shell-job',
|
||||
state: 'zombie',
|
||||
startedAtMs: life.createdAtMs,
|
||||
createdAtMs: life.createdAtMs,
|
||||
lastStateChangeAtMs: life.lastStateChangeAtMs,
|
||||
completedAtMs: life.completedAtMs,
|
||||
jobId: jid,
|
||||
role: 'shell_job',
|
||||
lifecyclePhase: 'closed',
|
||||
label: typeof j.label === 'string' ? j.label.slice(0, 256) : undefined,
|
||||
cwd: '/',
|
||||
fds: [],
|
||||
...v8m(pid)
|
||||
}
|
||||
})
|
||||
const stdioTable = [
|
||||
{ fd: 0, kind: 'stdio', target: '/dev/stdin' },
|
||||
{ fd: 1, kind: 'stdio', target: '/dev/stdout' },
|
||||
{ fd: 2, kind: 'stdio', target: '/dev/stderr' }
|
||||
]
|
||||
const extraFdTable = logicalRows.map((r) => ({
|
||||
fd: r.fd | 0,
|
||||
kind: 'logical',
|
||||
target: String(r.target || '').slice(0, 1024)
|
||||
}))
|
||||
|
||||
const sigHandlers =
|
||||
opts.sigactionHandlers && typeof opts.sigactionHandlers === 'object'
|
||||
? opts.sigactionHandlers
|
||||
: null
|
||||
|
||||
const env = opts.env && typeof opts.env === 'object' ? opts.env : {}
|
||||
const pipefailOn =
|
||||
env.BARE_OS_SHELL_PIPEFAIL === '1' ||
|
||||
env.BARE_OS_SHELL_PIPEFAIL === 'true'
|
||||
const posixModeOn =
|
||||
env.BARE_OS_SHELL_POSIX_MODE === '1' ||
|
||||
env.BARE_OS_SHELL_POSIX_MODE === 'true'
|
||||
|
||||
const initd = opts.initdReadiness
|
||||
const initdUnitsRaw =
|
||||
initd && Array.isArray(initd.units) ? initd.units : []
|
||||
/** @type {Array<Record<string, unknown>>} */
|
||||
const initdLogicalProcs = []
|
||||
let i = 0
|
||||
for (const u of initdUnitsRaw) {
|
||||
if (!u || typeof u.name !== 'string') continue
|
||||
const pid = syntheticInitdPidFor(u.name)
|
||||
i++
|
||||
const pgid = pid
|
||||
const nmLower = u.name.toLowerCase()
|
||||
const replicationHint =
|
||||
u.phase === 'failed' &&
|
||||
/repl|swarm|net|peer|dht|hdms|hyperswarm/.test(nmLower)
|
||||
? 'net-related-initd-failure'
|
||||
: undefined
|
||||
const life = observeInitdLifecycle(
|
||||
u.name,
|
||||
String(u.phase || ''),
|
||||
typeof u.startedAtMs === 'number' ? u.startedAtMs : now,
|
||||
now
|
||||
)
|
||||
initdLogicalProcs.push({
|
||||
pid,
|
||||
ppid: 2,
|
||||
pgid,
|
||||
sid: 1,
|
||||
name: `bare-initd:${u.name.slice(0, 64)}`,
|
||||
state:
|
||||
u.phase === 'active'
|
||||
? 'running'
|
||||
: u.phase === 'failed'
|
||||
? 'signaled'
|
||||
: u.phase === 'starting'
|
||||
? 'running'
|
||||
: u.phase === 'stopping'
|
||||
? 'sleeping'
|
||||
: u.phase === 'skipped'
|
||||
? 'sleeping'
|
||||
: 'sleeping',
|
||||
startedAtMs: typeof u.startedAtMs === 'number' ? u.startedAtMs : now,
|
||||
createdAtMs: life.createdAtMs,
|
||||
lastStateChangeAtMs: life.lastStateChangeAtMs,
|
||||
completedAtMs: life.completedAtMs,
|
||||
role: 'initd_unit',
|
||||
initdUnit: u.name.slice(0, 128),
|
||||
initdPhase: String(u.phase || '').slice(0, 32),
|
||||
lastSignal: u.error ? 'error' : undefined,
|
||||
cwd: '/',
|
||||
fds: [],
|
||||
fdTable: [],
|
||||
...v8m(pid),
|
||||
...(replicationHint ? { replicationHint } : {})
|
||||
})
|
||||
}
|
||||
|
||||
const baseProcesses = [
|
||||
{ ...mk(1, 0, 'bare-os-kernel', boot || now, 1, 1), cwd: '/', fds: [], fdTable: [] },
|
||||
{ ...mk(2, 1, 'bare-os-booter', boot || now, 2, 1), cwd: '/', fds: [], fdTable: [] },
|
||||
{
|
||||
...mk(3, 2, 'bare-os-shell', now, 3, 1),
|
||||
sessionId: sid || undefined,
|
||||
cwd: '/',
|
||||
fds: [0, 1, 2],
|
||||
fdTable: [...stdioTable, ...extraFdTable],
|
||||
role: 'session_shell'
|
||||
},
|
||||
...initdLogicalProcs,
|
||||
...jobProcs.map((p) => ({
|
||||
...p,
|
||||
cwd: typeof p.cwd === 'string' ? p.cwd : '/',
|
||||
fds: Array.isArray(p.fds) ? p.fds : [0, 1, 2],
|
||||
fdTable: [...stdioTable],
|
||||
threads: p.threads ?? 1
|
||||
})),
|
||||
...zombieProcs
|
||||
]
|
||||
const pidToName = new Map()
|
||||
for (const p of baseProcesses) {
|
||||
if (p && typeof p.pid === 'number' && typeof p.name === 'string')
|
||||
pidToName.set(p.pid, p.name)
|
||||
}
|
||||
const processes = baseProcesses.map((p) => ({
|
||||
...p,
|
||||
parentName:
|
||||
typeof p.ppid === 'number' && pidToName.has(p.ppid)
|
||||
? pidToName.get(p.ppid)
|
||||
: undefined
|
||||
}))
|
||||
|
||||
return {
|
||||
schema: 9,
|
||||
schemaVersion: 10,
|
||||
note: 'Synthetic rows; Bare OS guests do not expose host OS processes. v10 adds lifecycle timestamps (createdAtMs/lastStateChangeAtMs/completedAtMs) for shell jobs and initd units. v9 adds per-row nice (-20..19) via ctx.bareOsRenice, accountingSource on accounting fields, and companions process_maps.json / process_threads.json (logical models). v8 optional accounting + initd replicationHint retained.',
|
||||
signalModel: {
|
||||
schema: 1,
|
||||
note: 'Virtual deliveries via ctx.bareOsSendSignal (not full sigaction/sigprocmask).',
|
||||
sigpendingAnalog: virtualSignalDeliveries,
|
||||
sigprocmaskAnalog: []
|
||||
},
|
||||
signalRouting: {
|
||||
schema: 1,
|
||||
bareOsSendSignal:
|
||||
'Delivers to synthetic pids 1–3 (kernel, booter, shell), shell job rows (41xx), and initd logical rows (51xx) when keyed by pid; see bareOsSendSignal implementation.',
|
||||
killUtility:
|
||||
'/bin/kill maps names kernel|booter|shell, numeric pids, and **negative pgid** (e.g. `kill -TERM -300` for logical group 300) to the same routing.',
|
||||
trap:
|
||||
'Shell builtins use ctx.shellTrapHandlers; signals are synthetic (no host kill).',
|
||||
sessionShellPgid: 3
|
||||
},
|
||||
sigactionSurface: {
|
||||
schema: 1,
|
||||
note: 'Logical dispositions via ctx.bareOsSigaction(signal, IGNORE|DEFAULT); IGNORE suppresses delivery and session exit side effects.',
|
||||
handlers:
|
||||
sigHandlers && Object.keys(sigHandlers).length
|
||||
? { ...sigHandlers }
|
||||
: {}
|
||||
},
|
||||
exitStatusModel: {
|
||||
schema: 1,
|
||||
note: 'POSIX-like: pipeline exit is last stage exitCode; && stops on non-zero prior; || stops on zero prior.',
|
||||
timeoutExit124:
|
||||
'When BARE_OS_FEATURE_ABORT_TIMEOUT is advertised, /bin/timeout uses exit 124 on wall timeout.',
|
||||
signalTerminated:
|
||||
'Synthetic kills set high-bit style exit (128+n) only where shell documents; see shell + kernel-runner tests.'
|
||||
},
|
||||
session: {
|
||||
sessionId: sid || undefined,
|
||||
controllingTty: undefined,
|
||||
foregroundPgid: 3,
|
||||
sid: 1
|
||||
},
|
||||
processGroups: {
|
||||
schema: 1,
|
||||
note: 'Synthetic pgid/sid on shell jobs approximate POSIX process groups; guests have no host PIDs.',
|
||||
setpgidAnalog:
|
||||
'Background job start assigns pgid on shellBackgroundJobs rows where implemented.',
|
||||
killpgAnalog:
|
||||
'Negative numeric targets index **`shellBackgroundJobs`** by **`pgid`**; active jobs only (zombies excluded except **`kill -0`** existence checks). Not a full killpg(2) emulation.'
|
||||
},
|
||||
jobControlSemantics: {
|
||||
schema: 1,
|
||||
pipefail: pipefailOn,
|
||||
posixShellMode: posixModeOn,
|
||||
pipelineExitRule: pipefailOn
|
||||
? 'first_failing_stage_in_pipeline'
|
||||
: 'last_stage_posix_default',
|
||||
waitReapsCompletedJobs: true,
|
||||
waitExitCodeRule:
|
||||
'When background jobs record lastExitCode, wait uses last non-zero exit among waited jobs (all-target wait); wait -n uses the first completed job exit.',
|
||||
note: 'Mirrors BARE_OS_SHELL_PIPEFAIL and BARE_OS_SHELL_POSIX_MODE; see handbook ch.9 §3.'
|
||||
},
|
||||
ipcProcessGroups:
|
||||
opts.ipcStats &&
|
||||
opts.ipcStats.processGroups &&
|
||||
typeof opts.ipcStats.processGroups === 'object'
|
||||
? opts.ipcStats.processGroups
|
||||
: undefined,
|
||||
initdBinding: {
|
||||
schema: 1,
|
||||
note: 'initd units mirrored as logical pids 5100+ for POSIX-like ps/kill introspection.',
|
||||
readinessSchema: initd?.schema,
|
||||
unitCount: initdLogicalProcs.length
|
||||
},
|
||||
rlimits: {
|
||||
note: 'Logical defaults; host rlimits may differ when subprocess bridge is active.',
|
||||
nofile: { soft: 1024, hard: 4096 },
|
||||
stack: { soft: 8388608, hard: 8388608 }
|
||||
},
|
||||
cwd: '/',
|
||||
fdSummary: {
|
||||
nextFd: 3 + extraFdTable.length,
|
||||
reservedStdio: [0, 1, 2],
|
||||
note: 'Additional FDs may appear when pseudo-files or IPC channels are opened.'
|
||||
},
|
||||
jobStats: {
|
||||
running: jobProcs.filter((j) => !j.stopped).length,
|
||||
stopped: jobProcs.filter((j) => j.stopped).length,
|
||||
zombie: zombieJobs.length
|
||||
},
|
||||
subprocessBridgeMeta: subprocessBridgeMetaFromEnv(env),
|
||||
processes,
|
||||
atMs: now
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Best-effort orphan detector for logical worker/subprocess rows.
|
||||
* @param {ReturnType<typeof bareOsProcessTableSnapshot>} snap
|
||||
*/
|
||||
export function bareOsFindOrphanProcessRows(snap) {
|
||||
const rows = Array.isArray(snap?.processes) ? snap.processes : []
|
||||
const pids = new Set(rows.map((r) => Number(r && r.pid)).filter(Number.isFinite))
|
||||
return rows.filter((r) => {
|
||||
const ppid = Number(r && r.ppid)
|
||||
if (!Number.isFinite(ppid) || ppid <= 0) return false
|
||||
if (ppid === 1 || ppid === 2 || ppid === 3) return false
|
||||
return !pids.has(ppid)
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user