This commit is contained in:
Raven Scott
2026-04-03 20:01:26 -04:00
parent eb6e8260cf
commit 0334ece75a
14 changed files with 415 additions and 63 deletions
+1 -1
View File
@@ -14,7 +14,7 @@ Files in this directory are **read from disk by the seeder** (or copied into `pa
## Contents
- **`init.js`** — Kernel entry: must define `async function start(ctx)`. Boot order: **`/etc/os-release`** → **`/etc/motd`** → optional **`/etc/bare-os/rc.profile.<profile>`** (profile from **`BARE_OS_BOOT_PROFILE`** or first line of **`/etc/bare-os/profile`**; the booter mirrors the resolved name in **`ctx.env.BARE_OS_BOOT_PROFILE_RESOLVED`** and **`/run/bare-os/boot_profile`**) → **`/etc/bare-os/rc`** → **`/etc/bare-os/rc.d/*`** (sorted; only names starting with a digit, plus skips dotfiles, `*~`, `README*`, `*.md`) → optional **`/etc/bare-os/rc.local`** → **`/etc/bare-os/kernel.d/*`** (same rules as **`rc.d`**) → banner → when **`BARE_OS_SKIP_REPL`**, optional **onboot** lines from **`BARE_OS_ONBOOT`** (newline-separated) or **`/etc/bare-os/onboot`** (file order)**`readLine` / `execLine`** loop (boot snippet errors are logged, not fatal). Custom kernels may call **`ctx.registerKernelShutdownHook(fn)`** before initd disposers; use **`ctx.bareOsRuntimeCaps`** for limits, pseudo paths, and **`features`** ([`developer-guide/02-the-context-object.md`](../developer-guide/02-the-context-object.md)).
- **`init.js`** — Kernel entry: must define `async function start(ctx)`. Boot order: **`/etc/os-release`** → **`/etc/motd`** → optional **`/etc/bare-os/rc.profile.<profile>`** (profile from **`BARE_OS_BOOT_PROFILE`** or first line of **`/etc/bare-os/profile`**; the booter mirrors the resolved name in **`ctx.env.BARE_OS_BOOT_PROFILE_RESOLVED`** and **`/run/bare-os/boot_profile`**) → **`/etc/bare-os/rc`** → **`/etc/bare-os/rc.d/*`** (sorted; digit-prefixed names only; skip dotfiles, `*~`, `README*`, `*.md`; optional **`BARE_OS_RC_D_SKIP`** comma list and **`prefix*`** patterns) → optional **`/etc/bare-os/rc.local`** → **`/etc/bare-os/kernel.d/*`** (same rules as **`rc.d`**) → banner → when **`BARE_OS_SKIP_REPL`**, optional **onboot** lines from **`BARE_OS_ONBOOT`** or **`/etc/bare-os/onboot`** → **`readLine` / `execLine`** loop. Boot **`execLine`** errors in trusted snippets are logged; with **`BARE_OS_BOOT_STRICT=1`** or **`true`**, the first throw calls **`requestBooterExit(1)`** and stops later boot phases. Custom kernels may call **`ctx.registerKernelShutdownHook(fn)`** before initd disposers; use **`ctx.bareOsRuntimeCaps`** for limits, pseudo paths, and **`features`** ([`developer-guide/02-the-context-object.md`](../developer-guide/02-the-context-object.md)).
- **`bin/`** — **Tier-1 utilities** built by [bare-os-coreutils](../packages/bare-os-coreutils/README.md). Each file is **`runtime.js`** + optional **`lib/*-engine.js`** (**`sed`**, **`awk`**) or **`lib/man-render.js`** (**`man`**) + **`async function run(ctx, argv)`** (no ESM **`import`** in **`src/`**).
- **`share/man/man.json`** — Merged manual database for **`/bin/man`** (built by **`bare-os-coreutils`**; see [handbook ch.10](../handbook/10-manpages-and-online-help.md)).
- **`etc/os-release`** — Static OS metadata (`NAME`, `VERSION`, …).
+103 -18
View File
@@ -16,6 +16,12 @@
*
* BARE_OS_BOOT_TRACE=json logs one JSON object per phase on stderr: {"phase":"…","ms":n}.
*
* BARE_OS_BOOT_STRICT=1 or true: first execLine throw in trusted boot snippets calls
* requestBooterExit(1) and stops further boot phases.
*
* BARE_OS_RC_D_SKIP: comma-separated rc.d basenames to skip; a pattern ending with * skips
* names with that prefix (e.g. 10-* skips 10-foo).
*
* Use ctx.registerKernelShutdownHook(fn) for teardown before initd disposers.
*/
@@ -34,6 +40,42 @@ function isBootTraceJson(ctx) {
return ctx.env && ctx.env.BARE_OS_BOOT_TRACE === 'json'
}
/**
* @param {Record<string, unknown>} ctx
*/
function bootStrict(ctx) {
const v = ctx.env && ctx.env.BARE_OS_BOOT_STRICT
return v === '1' || v === 'true'
}
/**
* @param {Record<string, unknown>} ctx
* @returns {string[]}
*/
function parseRcDSkipPatterns(ctx) {
const raw = ctx.env && ctx.env.BARE_OS_RC_D_SKIP
if (raw == null || !String(raw).trim()) return []
return String(raw)
.split(',')
.map((s) => s.trim())
.filter(Boolean)
}
/**
* @param {string} name
* @param {string[]} patterns
*/
function shouldSkipRcDName(name, patterns) {
for (const p of patterns) {
if (p === name) return true
if (p.endsWith('*') && p.length > 1) {
const pre = p.slice(0, -1)
if (name.startsWith(pre)) return true
}
}
return false
}
/**
* @param {Record<string, unknown>} ctx
* @param {string} label
@@ -55,9 +97,11 @@ async function bootTimed(ctx, label, fn) {
/**
* @param {Record<string, unknown>} ctx
* @param {string} text
* @returns {Promise<boolean>} false if BARE_OS_BOOT_STRICT and a line threw
*/
async function runRcLines(ctx, text) {
const { execLine, console } = ctx
const strict = bootStrict(ctx)
for (const line of text.split(/\r?\n/)) {
const t = line.trim()
if (!t || t.startsWith('#')) continue
@@ -65,8 +109,13 @@ async function runRcLines(ctx, text) {
await execLine(t)
} catch (e) {
console.error((e && e.message) || String(e))
if (strict) {
if (typeof ctx.requestBooterExit === 'function') ctx.requestBooterExit(1)
return false
}
}
}
return true
}
/**
@@ -118,17 +167,18 @@ async function resolveBootProfileName(ctx) {
* Optional trusted snippet /etc/bare-os/rc.profile.<name> (before /etc/bare-os/rc).
* @param {Record<string, unknown>} ctx
* @param {string} profileName
* @returns {Promise<boolean>}
*/
async function runProfileRc(ctx, profileName) {
if (!profileName) return
if (!profileName) return true
const safe = profileName.replace(/[^a-zA-Z0-9._-]/g, '')
if (safe !== profileName) {
ctx.console.error(
'[boot] profile name contains unsupported characters; skipping rc.profile'
)
return
return true
}
await runRcFileAt(
return await runRcFileAt(
ctx,
`/etc/bare-os/rc.profile.${safe}`,
`rc.profile.${safe}`
@@ -138,10 +188,12 @@ async function runProfileRc(ctx, profileName) {
/**
* When stdin is non-interactive, run trusted boot commands (automation).
* @param {Record<string, unknown>} ctx
* @returns {Promise<boolean>}
*/
async function runOnboot(ctx) {
if (!ctx.bareOsSkipRepl) return
if (!ctx.bareOsSkipRepl) return true
const { execLine, console, drive, b4a, env } = ctx
const strict = bootStrict(ctx)
/** @type {string[]} */
const lines = []
const fromEnv = env && env.BARE_OS_ONBOOT
@@ -170,23 +222,30 @@ async function runOnboot(ctx) {
await execLine(line)
} catch (e) {
console.error((e && e.message) || String(e))
if (strict) {
if (typeof ctx.requestBooterExit === 'function') ctx.requestBooterExit(1)
return false
}
}
}
return true
}
/**
* @param {Record<string, unknown>} ctx
* @param {string} drivePath absolute path on system drive
* @param {string} label for errors
* @returns {Promise<boolean>}
*/
async function runRcFileAt(ctx, drivePath, label) {
const { drive, b4a, console } = ctx
try {
const buf = await drive.get(drivePath)
if (!buf) return
await runRcLines(ctx, b4a.toString(buf))
if (!buf) return true
return await runRcLines(ctx, b4a.toString(buf))
} catch (e) {
console.error(`${label}: ` + ((e && e.message) || String(e)))
return true
}
}
@@ -205,6 +264,7 @@ function isBareOsRcSnippetFile(name) {
/**
* Optional snippets under /etc/bare-os/kernel.d/ — same rules as rc.d; runs after rc.local.
* @param {Record<string, unknown>} ctx
* @returns {Promise<boolean>}
*/
async function runBareOsKernelDir(ctx) {
const { drive, b4a, console } = ctx
@@ -214,7 +274,7 @@ async function runBareOsKernelDir(ctx) {
try {
for await (const n of drive.readdir('/etc/bare-os/kernel.d')) names.push(n)
} catch {
return
return true
}
names.sort()
for (const name of names) {
@@ -223,7 +283,8 @@ async function runBareOsKernelDir(ctx) {
try {
const buf = await drive.get(p)
if (!buf) continue
await runRcLines(ctx, b4a.toString(buf))
const cont = await runRcLines(ctx, b4a.toString(buf))
if (!cont) return false
} catch (e) {
console.error(`kernel.d/${name}: ` + ((e && e.message) || String(e)))
}
@@ -231,30 +292,35 @@ async function runBareOsKernelDir(ctx) {
} catch (e) {
console.error((e && e.message) || String(e))
}
return true
}
/**
* Optional snippets under /etc/bare-os/rc.d/ — executed in lexicographic order.
* @param {Record<string, unknown>} ctx
* @returns {Promise<boolean>}
*/
async function runBareOsRcDir(ctx) {
const { drive, b4a, console } = ctx
const skip = parseRcDSkipPatterns(ctx)
try {
/** @type {string[]} */
const names = []
try {
for await (const n of drive.readdir('/etc/bare-os/rc.d')) names.push(n)
} catch {
return
return true
}
names.sort()
for (const name of names) {
if (!isBareOsRcSnippetFile(name)) continue
if (shouldSkipRcDName(name, skip)) continue
const p = `/etc/bare-os/rc.d/${name}`
try {
const buf = await drive.get(p)
if (!buf) continue
await runRcLines(ctx, b4a.toString(buf))
const cont = await runRcLines(ctx, b4a.toString(buf))
if (!cont) return false
} catch (e) {
console.error(`rc.d/${name}: ` + ((e && e.message) || String(e)))
}
@@ -262,6 +328,7 @@ async function runBareOsRcDir(ctx) {
} catch (e) {
console.error((e && e.message) || String(e))
}
return true
}
/**
@@ -296,15 +363,33 @@ async function start(ctx) {
await bootTimed(ctx, 'os-release', () => printOsRelease(ctx))
await bootTimed(ctx, 'motd', () => printMotd(ctx))
const profileName = await resolveBootProfileName(ctx)
await bootTimed(ctx, 'rc.profile', () => runProfileRc(ctx, profileName))
await bootTimed(ctx, 'rc', () => runRcFileAt(ctx, '/etc/bare-os/rc', 'rc'))
await bootTimed(ctx, 'rc.d', () => runBareOsRcDir(ctx))
await bootTimed(ctx, 'rc.local', () =>
runRcFileAt(ctx, '/etc/bare-os/rc.local', 'rc.local')
)
await bootTimed(ctx, 'kernel.d', () => runBareOsKernelDir(ctx))
/** @type {boolean} */
let bootOk = true
await bootTimed(ctx, 'rc.profile', async () => {
bootOk = await runProfileRc(ctx, profileName)
})
if (!bootOk) return
await bootTimed(ctx, 'rc', async () => {
bootOk = await runRcFileAt(ctx, '/etc/bare-os/rc', 'rc')
})
if (!bootOk) return
await bootTimed(ctx, 'rc.d', async () => {
bootOk = await runBareOsRcDir(ctx)
})
if (!bootOk) return
await bootTimed(ctx, 'rc.local', async () => {
bootOk = await runRcFileAt(ctx, '/etc/bare-os/rc.local', 'rc.local')
})
if (!bootOk) return
await bootTimed(ctx, 'kernel.d', async () => {
bootOk = await runBareOsKernelDir(ctx)
})
if (!bootOk) return
await printSessionBanner(ctx)
await bootTimed(ctx, 'onboot', () => runOnboot(ctx))
await bootTimed(ctx, 'onboot', async () => {
bootOk = await runOnboot(ctx)
})
if (!bootOk) return
while (true) {
const line = await readLine('')
if (line == null) break
File diff suppressed because one or more lines are too long