163 lines
5.0 KiB
JavaScript
163 lines
5.0 KiB
JavaScript
/**
|
||
* Synthetic process signal and nice methods on kernel `ctx`.
|
||
*/
|
||
import {
|
||
bareOsIsPosixSignalName,
|
||
bareOsNormalizeSignalName
|
||
} from './bare-os-posix-signals.js'
|
||
|
||
/**
|
||
* @param {{
|
||
* deliverBareOsVirtualSignalToPid: (ctx: unknown, pid: number, sig: string) => { ignored?: boolean, delivered?: boolean, atMs?: number },
|
||
* getInteractiveCtx: () => Record<string, unknown> | null | undefined,
|
||
* niceByPid: Map<number, number>
|
||
* }} deps
|
||
*/
|
||
export function createBareOsProcessControlMethods(deps) {
|
||
const { deliverBareOsVirtualSignalToPid, getInteractiveCtx, niceByPid } = deps
|
||
|
||
return {
|
||
/**
|
||
* POSIX-like signal dispatch over synthetic process IDs.
|
||
* Targets: numeric pid (**1–3** session stack, **4100+** shell jobs, **5100+** initd logical rows), **negative process group** (e.g. `-300` → all running shell jobs with `pgid` 300), or `kernel` / `booter` / `shell`.
|
||
* Signals: HUP, INT, KILL, TERM, PIPE, CHLD, USR1, USR2; `PIPE`/`CHLD`/`USR*` update state only (no session exit).
|
||
*/
|
||
bareOsSendSignal(target, signal = 'TERM') {
|
||
const sig = bareOsNormalizeSignalName(signal)
|
||
if (!bareOsIsPosixSignalName(signal)) {
|
||
throw new Error('bareOsSendSignal: unsupported signal')
|
||
}
|
||
const raw = String(target == null ? '' : target).trim()
|
||
let pid = Number.parseInt(raw, 10)
|
||
let pgidTarget = null
|
||
if (Number.isFinite(pid) && pid < 0) {
|
||
pgidTarget = -pid
|
||
pid = NaN
|
||
}
|
||
if (!Number.isFinite(pid)) {
|
||
if (pgidTarget == null) {
|
||
if (raw === 'kernel') pid = 1
|
||
else if (raw === 'booter') pid = 2
|
||
else if (raw === 'shell') pid = 3
|
||
}
|
||
}
|
||
if (pgidTarget != null) {
|
||
const live = getInteractiveCtx()
|
||
const list =
|
||
live &&
|
||
live.shellBackgroundJobs &&
|
||
typeof live.shellBackgroundJobs === 'object' &&
|
||
Array.isArray(live.shellBackgroundJobs.list)
|
||
? live.shellBackgroundJobs.list
|
||
: []
|
||
const byPgid = list.filter(
|
||
(j) =>
|
||
j &&
|
||
typeof j.pgid === 'number' &&
|
||
Math.floor(j.pgid) === Math.floor(pgidTarget)
|
||
)
|
||
const active = byPgid.filter((j) => !j.done)
|
||
if (sig === '0') {
|
||
if (byPgid.length === 0) {
|
||
throw new Error('bareOsSendSignal: unknown process group')
|
||
}
|
||
return {
|
||
ok: true,
|
||
pgid: pgidTarget,
|
||
signal: sig,
|
||
delivered: false,
|
||
exists: true,
|
||
jobCount: byPgid.length
|
||
}
|
||
}
|
||
if (active.length === 0) {
|
||
throw new Error('bareOsSendSignal: unknown process group')
|
||
}
|
||
if (
|
||
this.bareOsLogicalSigaction &&
|
||
this.bareOsLogicalSigaction[sig] === 'IGNORE'
|
||
) {
|
||
return {
|
||
ok: true,
|
||
pgid: pgidTarget,
|
||
signal: sig,
|
||
delivered: false,
|
||
ignored: true,
|
||
atMs: Date.now()
|
||
}
|
||
}
|
||
const atMs = Date.now()
|
||
/** @type {number[]} */
|
||
const pids = []
|
||
for (const j of active) {
|
||
const jp = 4100 + j.id
|
||
pids.push(jp)
|
||
deliverBareOsVirtualSignalToPid(this, jp, sig)
|
||
}
|
||
return {
|
||
ok: true,
|
||
pgid: pgidTarget,
|
||
signal: sig,
|
||
delivered: true,
|
||
atMs,
|
||
pids
|
||
}
|
||
}
|
||
const extendedSynthetic =
|
||
Number.isFinite(pid) && pid >= 4100 && pid < 9000
|
||
if (!Number.isFinite(pid) || pid < 1 || (pid > 3 && !extendedSynthetic)) {
|
||
throw new Error('bareOsSendSignal: unknown process target')
|
||
}
|
||
if (sig === '0') {
|
||
return { ok: true, pid, signal: sig, delivered: false, exists: true }
|
||
}
|
||
const r = deliverBareOsVirtualSignalToPid(this, pid, sig)
|
||
if (r.ignored) {
|
||
return {
|
||
ok: true,
|
||
pid,
|
||
signal: sig,
|
||
delivered: false,
|
||
ignored: true,
|
||
atMs: r.atMs
|
||
}
|
||
}
|
||
return {
|
||
ok: true,
|
||
pid,
|
||
signal: sig,
|
||
delivered: !!r.delivered,
|
||
atMs: r.atMs
|
||
}
|
||
},
|
||
/**
|
||
* Logical nice adjustment for synthetic PIDs (stored on **`process_table.json`** rows; host scheduler is not affected).
|
||
* @param {number} pid
|
||
* @param {number} [delta]
|
||
*/
|
||
bareOsRenice(pid, delta = 0) {
|
||
const p = Number(pid)
|
||
const d = Number(delta)
|
||
if (!Number.isFinite(p) || p < 1) {
|
||
throw new Error('bareOsRenice: invalid pid')
|
||
}
|
||
if (!Number.isFinite(d)) {
|
||
throw new Error('bareOsRenice: invalid delta')
|
||
}
|
||
const clampNice = (n) =>
|
||
Math.max(-20, Math.min(19, Math.trunc(Number.isFinite(n) ? n : 0)))
|
||
const cur = clampNice(niceByPid.get(p) ?? 0)
|
||
const next = clampNice(cur + d)
|
||
niceByPid.set(p, next)
|
||
return {
|
||
ok: true,
|
||
pid: p,
|
||
delta: d,
|
||
nice: next,
|
||
previousNice: cur,
|
||
atMs: Date.now()
|
||
}
|
||
}
|
||
}
|
||
}
|