Orginize
This commit is contained in:
@@ -0,0 +1,366 @@
|
||||
/**
|
||||
* bare-holesail — Holesail TCP/UDP P2P proxy via the official holesail package API only.
|
||||
* Initd unit `bare-holesail` (stock-on managed mode; disable via env/initd) and optional early booter instance (kernel-path).
|
||||
*
|
||||
* Upstream is AGPL-3.0; the booter imports `holesail` directly (same Pear-staged pattern as
|
||||
* bare-openssh / bare-ssh2) instead of depending on ctx.bare / drive-bundle resolution.
|
||||
*/
|
||||
import { appendVarLog, BARE_OS_VAR_LOG_DIR } from './bare-os-var-log.js'
|
||||
import { bareHolesailEnvTruthy } from './bare-holesail-env.js'
|
||||
import {
|
||||
bareHolesailManagedServiceIsRunning,
|
||||
bareHolesailManagedStartService,
|
||||
bareHolesailManagedStopService
|
||||
} from './bare-holesail-managed.js'
|
||||
import { bindFallbackEnabled, isAddrInUse } from './bare-os-bind-fallback.js'
|
||||
import { loadHolesailConstructor } from './bare-holesail-loader.js'
|
||||
import {
|
||||
registerBareInitdDisposer,
|
||||
registerBareService,
|
||||
registerKernelShutdownHook
|
||||
} from './bare-initd.js'
|
||||
|
||||
export { bareHolesailEnvTruthy }
|
||||
|
||||
export const BARE_HOLESAIL_LOG = `${BARE_OS_VAR_LOG_DIR}/holesail.log`
|
||||
|
||||
/** @type {unknown} */
|
||||
let kernelHolesail = null
|
||||
/** @type {unknown} */
|
||||
let initdHolesail = null
|
||||
/** @type {(() => void)[]} */
|
||||
let initdHookUnsubs = []
|
||||
/** @type {(() => void)[]} */
|
||||
let kernelHookUnsubs = []
|
||||
|
||||
/** @type {boolean} */
|
||||
let bareHolesailInitdRegistered = false
|
||||
|
||||
/**
|
||||
* Register the **bare-holesail** initd unit (call from **`bare-user-session-stack`** after unlock).
|
||||
* Stock **`BARE_OS_HOLESAIL_INITD`** default is on; set **`0`** to skip registration.
|
||||
* @param {Record<string, string | undefined>} env
|
||||
*/
|
||||
export function maybeRegisterBareHolesailInitd(env) {
|
||||
if (bareHolesailInitdRegistered) return
|
||||
if (!bareHolesailEnvTruthy(env.BARE_OS_HOLESAIL_INITD)) return
|
||||
bareHolesailInitdRegistered = true
|
||||
registerBareService({
|
||||
name: 'bare-holesail',
|
||||
description:
|
||||
'Holesail P2P TCP/UDP proxy (official holesail API); on by default (managed multi-tunnel for /bin/holesail); disable with BARE_OS_HOLESAIL_INITD=0 or systemctl disable',
|
||||
logPath: BARE_HOLESAIL_LOG,
|
||||
start: initdStartBareHolesail,
|
||||
stop: initdStopBareHolesail
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Record<string, string | undefined>} env
|
||||
* @param {string} key
|
||||
*/
|
||||
function envStr(env, key) {
|
||||
const v = env[key]
|
||||
if (v === undefined || v === null) return ''
|
||||
return String(v).trim()
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Record<string, string | undefined>} env
|
||||
* @param {'initd' | 'kernel'} profile
|
||||
* @returns {{ enabled: boolean, err?: string, opts?: Record<string, unknown> }}
|
||||
*/
|
||||
export function bareHolesailParseConfig(env, profile) {
|
||||
const isKernel = profile === 'kernel'
|
||||
const enabled = isKernel
|
||||
? bareHolesailEnvTruthy(env.BARE_OS_HOLESAIL_KERNEL)
|
||||
: bareHolesailEnvTruthy(env.BARE_OS_HOLESAIL_INITD)
|
||||
if (!enabled) return { enabled: false }
|
||||
|
||||
const P = isKernel ? 'BARE_OS_HOLESAIL_KERNEL_' : 'BARE_OS_HOLESAIL_'
|
||||
const server = bareHolesailEnvTruthy(env[`${P}SERVER`])
|
||||
const client = bareHolesailEnvTruthy(env[`${P}CLIENT`])
|
||||
if (server === client) {
|
||||
return {
|
||||
enabled: true,
|
||||
err: 'holesail: set exactly one of SERVER or CLIENT for this profile'
|
||||
}
|
||||
}
|
||||
const key = envStr(env, `${P}KEY`)
|
||||
if (client && !key) {
|
||||
return { enabled: true, err: 'holesail: CLIENT requires KEY' }
|
||||
}
|
||||
|
||||
/** @type {Record<string, unknown>} */
|
||||
const opts = {
|
||||
server,
|
||||
client,
|
||||
key: key || undefined
|
||||
}
|
||||
|
||||
const sec = envStr(env, `${P}SECURE`)
|
||||
if (sec !== '') opts.secure = bareHolesailEnvTruthy(sec)
|
||||
|
||||
const portRaw = envStr(env, `${P}PORT`)
|
||||
if (portRaw !== '') {
|
||||
const n = Number.parseInt(portRaw, 10)
|
||||
if (!Number.isFinite(n)) {
|
||||
return { enabled: true, err: 'holesail: invalid PORT' }
|
||||
}
|
||||
opts.port = n
|
||||
}
|
||||
|
||||
const host = envStr(env, `${P}HOST`)
|
||||
if (host !== '') opts.host = host
|
||||
|
||||
const udp = envStr(env, `${P}UDP`)
|
||||
if (udp !== '') opts.udp = bareHolesailEnvTruthy(udp)
|
||||
|
||||
const logRaw = envStr(env, `${P}LOG`)
|
||||
if (logRaw !== '') {
|
||||
if (/^\d+$/.test(logRaw)) opts.log = Number.parseInt(logRaw, 10)
|
||||
else opts.log = bareHolesailEnvTruthy(logRaw)
|
||||
}
|
||||
|
||||
return { enabled: true, opts }
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Record<string, unknown>} ctx
|
||||
* @param {string} line
|
||||
*/
|
||||
function logLine(ctx, line) {
|
||||
void appendVarLog(ctx, BARE_HOLESAIL_LOG, 'holesail', line)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Record<string, unknown>} ctx
|
||||
* @param {Record<string, string | undefined>} env
|
||||
*/
|
||||
function debugEnabled(ctx, env) {
|
||||
return bareHolesailEnvTruthy(env.BARE_OS_HOLESAIL_DEBUG)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {unknown} hs
|
||||
*/
|
||||
async function safeCloseHolesail(hs) {
|
||||
if (!hs || typeof hs.close !== 'function') return
|
||||
try {
|
||||
await hs.close()
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Optional early booter instance: runs after ctx is wired (repl phase), before bare-initd.
|
||||
* Env: BARE_OS_HOLESAIL_KERNEL=1 and BARE_OS_HOLESAIL_KERNEL_SERVER|CLIENT, etc.
|
||||
*
|
||||
* @param {Record<string, unknown>} ctx
|
||||
*/
|
||||
export async function maybeStartBareHolesailKernelFromBooter(ctx) {
|
||||
const env =
|
||||
ctx.env && typeof ctx.env === 'object'
|
||||
? /** @type {Record<string, string | undefined>} */ (ctx.env)
|
||||
: /** @type {Record<string, string | undefined>} */ ({})
|
||||
|
||||
const parsed = bareHolesailParseConfig(env, 'kernel')
|
||||
if (!parsed.enabled) return
|
||||
if (parsed.err) {
|
||||
logLine(ctx, `kernel: ${parsed.err}`)
|
||||
try {
|
||||
ctx.console?.error?.(`[bare-holesail] ${parsed.err}`)
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
return
|
||||
}
|
||||
if (!parsed.opts) return
|
||||
|
||||
try {
|
||||
const Holesail = await loadHolesailConstructor(ctx)
|
||||
let hs = new Holesail(parsed.opts)
|
||||
try {
|
||||
await hs.ready()
|
||||
} catch (e) {
|
||||
if (!bindFallbackEnabled(env) || !isAddrInUse(e)) throw e
|
||||
await safeCloseHolesail(hs)
|
||||
hs = new Holesail({ ...parsed.opts, port: 0 })
|
||||
await hs.ready()
|
||||
logLine(ctx, 'kernel: ready after EADDRINUSE fallback (ephemeral port)')
|
||||
}
|
||||
kernelHolesail = hs
|
||||
logLine(ctx, 'kernel: ready (early booter instance)')
|
||||
if (debugEnabled(ctx, env)) {
|
||||
try {
|
||||
const info = hs.info
|
||||
const url = info && typeof info === 'object' ? info.url : ''
|
||||
ctx.console?.log?.(`[bare-holesail] kernel url=${String(url).slice(0, 120)}`)
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
|
||||
kernelHookUnsubs.push(
|
||||
ctx.bareOsRegisterSuspendHook(async () => {
|
||||
if (kernelHolesail && typeof kernelHolesail.pause === 'function') {
|
||||
try {
|
||||
await kernelHolesail.pause()
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
kernelHookUnsubs.push(
|
||||
ctx.bareOsRegisterResumeHook(async () => {
|
||||
if (kernelHolesail && typeof kernelHolesail.resume === 'function') {
|
||||
try {
|
||||
await kernelHolesail.resume()
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
registerKernelShutdownHook(async () => {
|
||||
for (const u of kernelHookUnsubs.splice(0)) {
|
||||
try {
|
||||
u()
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
const h = kernelHolesail
|
||||
kernelHolesail = null
|
||||
await safeCloseHolesail(h)
|
||||
logLine(ctx, 'kernel: closed (session shutdown)')
|
||||
})
|
||||
} catch (e) {
|
||||
const msg = (e && /** @type {{ message?: string }} */ (e).message) || String(e)
|
||||
logLine(ctx, `kernel: start error: ${msg}`)
|
||||
try {
|
||||
ctx.console?.error?.(`[bare-holesail] kernel: ${msg}`)
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function initdStartBareHolesail(ctx) {
|
||||
const env =
|
||||
ctx.env && typeof ctx.env === 'object'
|
||||
? /** @type {Record<string, string | undefined>} */ (ctx.env)
|
||||
: /** @type {Record<string, string | undefined>} */ ({})
|
||||
|
||||
const initdOn = bareHolesailEnvTruthy(env.BARE_OS_HOLESAIL_INITD)
|
||||
const managedOn = bareHolesailEnvTruthy(env.BARE_OS_HOLESAIL_MANAGED)
|
||||
if (initdOn && managedOn) {
|
||||
try {
|
||||
await bareHolesailManagedStartService(ctx, env)
|
||||
} catch (e) {
|
||||
const msg = (e && /** @type {{ message?: string }} */ (e).message) || String(e)
|
||||
logLine(ctx, `initd managed: start error: ${msg}`)
|
||||
throw e
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
const parsed = bareHolesailParseConfig(env, 'initd')
|
||||
if (!parsed.enabled) return
|
||||
if (parsed.err) {
|
||||
logLine(ctx, `initd: ${parsed.err}`)
|
||||
throw new Error(parsed.err)
|
||||
}
|
||||
if (!parsed.opts) return
|
||||
|
||||
try {
|
||||
const Holesail = await loadHolesailConstructor(ctx)
|
||||
let hs = new Holesail(parsed.opts)
|
||||
try {
|
||||
await hs.ready()
|
||||
} catch (e) {
|
||||
if (!bindFallbackEnabled(env) || !isAddrInUse(e)) throw e
|
||||
await safeCloseHolesail(hs)
|
||||
hs = new Holesail({ ...parsed.opts, port: 0 })
|
||||
await hs.ready()
|
||||
logLine(ctx, 'initd: ready after EADDRINUSE fallback (ephemeral port)')
|
||||
}
|
||||
initdHolesail = hs
|
||||
logLine(ctx, 'initd: ready')
|
||||
if (debugEnabled(ctx, env)) {
|
||||
try {
|
||||
const info = hs.info
|
||||
const url = info && typeof info === 'object' ? info.url : ''
|
||||
ctx.console?.log?.(`[bare-holesail] initd url=${String(url).slice(0, 120)}`)
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
|
||||
initdHookUnsubs.push(
|
||||
ctx.bareOsRegisterSuspendHook(async () => {
|
||||
if (initdHolesail && typeof initdHolesail.pause === 'function') {
|
||||
try {
|
||||
await initdHolesail.pause()
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
initdHookUnsubs.push(
|
||||
ctx.bareOsRegisterResumeHook(async () => {
|
||||
if (initdHolesail && typeof initdHolesail.resume === 'function') {
|
||||
try {
|
||||
await initdHolesail.resume()
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
} catch (e) {
|
||||
const msg = (e && /** @type {{ message?: string }} */ (e).message) || String(e)
|
||||
logLine(ctx, `initd: start error: ${msg}`)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
async function initdStopBareHolesail(ctx) {
|
||||
if (bareHolesailManagedServiceIsRunning()) {
|
||||
await bareHolesailManagedStopService(ctx)
|
||||
logLine(ctx, 'initd: stopped (managed)')
|
||||
return
|
||||
}
|
||||
for (const u of initdHookUnsubs.splice(0)) {
|
||||
try {
|
||||
u()
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
const h = initdHolesail
|
||||
initdHolesail = null
|
||||
await safeCloseHolesail(h)
|
||||
logLine(ctx, 'initd: stopped')
|
||||
}
|
||||
|
||||
registerBareInitdDisposer(() => {
|
||||
if (bareHolesailManagedServiceIsRunning()) {
|
||||
void bareHolesailManagedStopService({})
|
||||
return
|
||||
}
|
||||
for (const u of initdHookUnsubs.splice(0)) {
|
||||
try {
|
||||
u()
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
const h = initdHolesail
|
||||
initdHolesail = null
|
||||
void safeCloseHolesail(h)
|
||||
})
|
||||
Reference in New Issue
Block a user