Files
bare-operating-system/packages/bare-os-booter/lib/posix/bare-os-syscall.js
T
2026-08-18 18:11:28 -04:00

2453 lines
77 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* POSIX-like syscall facade used as `ctx.bareOsSyscall`.
* Factory closes over executeKernel runtime objects; method body uses `this` as ctx.
*/
import b4a from 'b4a'
import { persistBareOsMountsSnapshot } from './bare-os-mounts-persist.js'
import {
bindFallbackEnabled,
isAddrInUse,
readListeningTcpPort
} from './bare-os-bind-fallback.js'
import {
bareOsTryAcquireCooperativeLock,
bareOsRemoveFcntlWaiter,
bareOsDrainFcntlLockWaiters
} from './bare-os-fcntl-cooperative-lock.js'
import {
wantPosixSocketFdBridge,
wantPosixFcntlBlockingWait,
bareOsPosixDgramRecvQueueMax,
bareOsPosixDgramRecvBlockMsMax,
bareOsPosixAcceptQueueMax,
bareOsPosixAcceptBlockMsMax,
bareOsFcntlLockWaitMsMax,
bareOsResolveCooperativeLockPath
} from './bare-os-posix-syscall-helpers.js'
import {
bareOsShutdownSocketBridgeFd,
prepareBridgeSendmsg
} from './bare-os-socket-bridge.js'
/**
* @param {{
* shellEnv: Record<string, string | undefined>,
* bootHrtimeNowNs: (() => bigint) | null,
* bareLibrary: Record<string, unknown>,
* bareOsIpc: unknown,
* hdmsController: unknown,
* vfs: unknown
* }} deps
*/
export function createBareOsSyscall(deps) {
const {
shellEnv,
bootHrtimeNowNs,
bareLibrary,
bareOsIpc,
hdmsController,
vfs
} = deps
/**
* Minimal syscall-shaped facade for POSIX-like userland adaptation.
* @param {string} op
* @param {Record<string, unknown>} [args]
*/
return async function bareOsSyscall(op, args = {}) {
const name = String(op || '').trim()
if (!name) throw new Error('bareOsSyscall: op is required')
if (name === 'pathconf') {
const pconfName = String(args.name || args.key || '').trim()
const v = this.bareOsPathconf(String(args.path || '/'), pconfName)
return { name: pconfName, value: v }
}
if (name === 'fsync' || name === 'fdatasync') {
return {
ok: true,
op: name,
posixAlignment: 'partial',
note: 'Guest logical fsync/fdatasync is a no-op; replication and block persistence are handled by the host Hyperdrive stack.'
}
}
if (name === 'posix_fadvise') {
const p = String(args.path || '').trim()
if (!p.startsWith('/')) {
throw new Error(
'bareOsSyscall: posix_fadvise: absolute path required'
)
}
return {
ok: true,
op: name,
path: p,
offset: Number(args.offset ?? 0) || 0,
len: Number(args.len ?? args.length ?? 0) || 0,
advice: Number(args.advice ?? args.adv ?? 0) || 0,
posixAlignment: 'simulated',
note: 'posix_fadvise is a logical no-op on Hyperdrive-backed paths; use for portable hints only.'
}
}
if (name === 'nanosleep') {
const sec = Number(args.seconds ?? args.tv_sec ?? 0)
const nsec = Number(args.nanoseconds ?? args.tv_nsec ?? args.ns ?? 0)
let ms =
(Number.isFinite(sec) ? sec : 0) * 1000 +
(Number.isFinite(nsec) ? nsec : 0) / 1e6
if (!Number.isFinite(ms) || ms < 0) ms = 0
const cap = 3_600_000
if (ms > cap) ms = cap
await new Promise((r) => setTimeout(r, ms))
return {
ok: true,
op: name,
sleptMs: Math.round(ms),
posixAlignment: 'partial',
note: 'POSIX-like delay via host setTimeout; capped at 3600s; not clock_nanosleep(2).'
}
}
if (name === 'clock_gettime') {
const raw = String(
args.clockId ?? args.clock_id ?? args.id ?? 'CLOCK_MONOTONIC'
).toUpperCase()
const isReal =
raw === 'REALTIME' ||
raw === 'CLOCK_REALTIME' ||
raw === '0' ||
raw === 'REAL_TIME'
let tvSec = 0
let tvNsec = 0
if (isReal) {
const ms = Date.now()
tvSec = Math.floor(ms / 1000)
tvNsec = (ms % 1000) * 1e6
} else if (bootHrtimeNowNs) {
const ns = bootHrtimeNowNs()
const bn = BigInt.asUintN(64, ns)
tvSec = Number(bn / 1000000000n)
tvNsec = Number(bn % 1000000000n)
} else {
try {
const perf = globalThis.performance
if (perf && typeof perf.now === 'function') {
const ms = perf.now()
tvSec = Math.floor(ms / 1000)
tvNsec = Math.round((ms % 1000) * 1e6)
} else {
const ms = Date.now()
tvSec = Math.floor(ms / 1000)
tvNsec = (ms % 1000) * 1e6
}
} catch {
const ms = Date.now()
tvSec = Math.floor(ms / 1000)
tvNsec = (ms % 1000) * 1e6
}
}
return {
ok: true,
op: name,
clockId: raw,
tv_sec: tvSec,
tv_nsec: tvNsec,
posixAlignment: 'partial',
monotonicSource: bootHrtimeNowNs
? 'bare-hrtime.bigint'
: globalThis.performance?.now
? 'performance.now'
: 'Date.now',
note: 'Partial clock_gettime: REALTIME wall; MONOTONIC prefers bare-hrtime else performance.now sketch; not CLOCK_TAI.'
}
}
if (name === 'mq_open') {
const ipc = bareOsIpc
if (!ipc || typeof ipc.mqOpen !== 'function') {
return {
ok: false,
op: name,
code: 'ENOSYS',
errnoHint: 'ENOSYS',
note: 'IPC message queues unavailable'
}
}
const qname = String(args.name || args.pathname || '').trim()
if (!qname) {
return {
ok: false,
op: name,
code: 'EINVAL',
errnoHint: 'EINVAL',
note: 'mq_open: name required'
}
}
const r = ipc.mqOpen(qname, {
maxmsg: args.maxmsg,
maxBytes: args.maxBytes
})
return { ok: true, op: name, ...r }
}
if (name === 'mq_send') {
const ipc = bareOsIpc
if (!ipc || typeof ipc.mqSend !== 'function') {
return {
ok: false,
op: name,
code: 'ENOSYS',
errnoHint: 'ENOSYS'
}
}
const qname = String(args.name || '').trim()
if (!qname) {
return {
ok: false,
op: name,
code: 'EINVAL',
errnoHint: 'EINVAL'
}
}
if (!ipc.mqAttrs(qname)) {
ipc.mqOpen(qname, {
maxmsg: args.maxmsg,
maxBytes: args.maxBytes
})
}
let body = args.data ?? args.buf ?? args.buffer
if (body == null) {
return {
ok: false,
op: name,
code: 'EINVAL',
errnoHint: 'EINVAL',
note: 'mq_send: data required'
}
}
if (typeof body === 'string') body = b4a.from(body, 'utf8')
if (!(body instanceof Uint8Array)) {
return {
ok: false,
op: name,
code: 'EINVAL',
errnoHint: 'EINVAL'
}
}
const prio = Number(args.prio) || 0
try {
ipc.mqSend(qname, prio, body)
return { ok: true, op: name, bytes: body.byteLength }
} catch (e) {
const em = e && /** @type {Error} */ (e).message
if (em && (em.includes('full') || em.includes('exceeds'))) {
return {
ok: false,
op: name,
code: 'EAGAIN',
errnoHint: 'EAGAIN',
posixAlignment: 'partial',
note: String(em)
}
}
throw e
}
}
if (name === 'mq_receive') {
const ipc = bareOsIpc
if (!ipc || typeof ipc.mqReceive !== 'function') {
return {
ok: false,
op: name,
code: 'ENOSYS',
errnoHint: 'ENOSYS'
}
}
const qname = String(args.name || '').trim()
if (!qname) {
return {
ok: false,
op: name,
code: 'EINVAL',
errnoHint: 'EINVAL'
}
}
const row = ipc.mqReceive(qname)
if (!row) {
return {
ok: false,
op: name,
code: 'EAGAIN',
errnoHint: 'EAGAIN',
posixAlignment: 'partial',
note: 'mq_receive: queue empty or unknown (mq_open first)'
}
}
return {
ok: true,
op: name,
prio: row.prio,
data: row.data,
bytesReceived: row.data.byteLength
}
}
if (name === 'readFile') {
const p = String(args.path || '').trim()
if (!p.startsWith('/'))
throw new Error('bareOsSyscall: absolute path required')
return vfs.readFile(p)
}
if (name === 'writeFile') {
const p = String(args.path || '').trim()
if (!p.startsWith('/'))
throw new Error('bareOsSyscall: absolute path required')
const d = args.data
const buf =
typeof d === 'string'
? b4a.from(d, 'utf8')
: d instanceof Uint8Array
? d
: d instanceof ArrayBuffer
? new Uint8Array(d)
: null
if (!buf)
throw new Error(
'bareOsSyscall: writeFile data must be string or bytes'
)
await vfs.writeFile(p, buf)
return { ok: true, path: p, bytes: buf.byteLength }
}
if (name === 'readdir') {
const p = String(args.path || '').trim()
if (!p.startsWith('/'))
throw new Error('bareOsSyscall: absolute path required')
return vfs.readdir(p)
}
if (name === 'mkdir') {
const p = String(args.path || '').trim()
if (!p.startsWith('/'))
throw new Error('bareOsSyscall: absolute path required')
const recursive = !!args.recursive
const mode =
args.mode == null ? undefined : Number.parseInt(String(args.mode), 10)
await vfs.mkdir(
p,
Number.isFinite(mode)
? { recursive, mode: Math.max(0, Math.min(0o777, mode)) }
: { recursive }
)
return { ok: true, path: p }
}
if (name === 'stat') {
const p = String(args.path || '').trim()
if (!p.startsWith('/'))
throw new Error('bareOsSyscall: absolute path required')
return vfs.stat(p)
}
if (name === 'unlink') {
const p = String(args.path || '').trim()
if (!p.startsWith('/'))
throw new Error('bareOsSyscall: absolute path required')
await vfs.unlink(p)
return { ok: true, path: p }
}
if (name === 'chmod') {
const p = String(args.path || '').trim()
if (!p.startsWith('/'))
throw new Error('bareOsSyscall: absolute path required')
const mode = Number.parseInt(String(args.mode || ''), 8)
if (!Number.isFinite(mode))
throw new Error('bareOsSyscall: chmod mode is required')
await vfs.chmod(p, mode)
return { ok: true, path: p, mode: mode & 0o777 }
}
if (name === 'chdir') {
const p = String(args.path || '').trim()
if (!p.startsWith('/'))
throw new Error('bareOsSyscall: absolute path required')
await vfs.chdir(p)
return { ok: true, cwd: vfs.getcwd() }
}
if (name === 'getcwd') {
return { cwd: vfs.getcwd() }
}
if (name === 'readlink') {
const p = String(args.path || '').trim()
if (!p.startsWith('/'))
throw new Error('bareOsSyscall: absolute path required')
const out = await vfs.readlink(p)
return { path: p, target: out }
}
if (name === 'symlink') {
const linkPath = String(args.path || '').trim()
const target = String(args.target || '').trim()
if (!linkPath.startsWith('/') || !target) {
throw new Error(
'bareOsSyscall: symlink requires absolute path and target'
)
}
await vfs.symlink(target, linkPath)
return { ok: true, path: linkPath, target }
}
if (name === 'exists') {
const p = String(args.path || '').trim()
if (!p.startsWith('/'))
throw new Error('bareOsSyscall: absolute path required')
return { path: p, exists: await vfs.exists(p) }
}
if (name === 'lstat') {
const p = String(args.path || '').trim()
if (!p.startsWith('/'))
throw new Error('bareOsSyscall: absolute path required')
return vfs.lstat(p)
}
if (name === 'rmdir') {
const p = String(args.path || '').trim()
if (!p.startsWith('/'))
throw new Error('bareOsSyscall: absolute path required')
await vfs.rmdir(p)
return { ok: true, path: p }
}
if (name === 'rename') {
const from = String(args.from || args.path || '').trim()
const to = String(args.to || '').trim()
if (!from.startsWith('/') || !to.startsWith('/')) {
throw new Error(
'bareOsSyscall: rename requires absolute from and to paths'
)
}
if (!(await vfs.exists(from))) {
throw new Error('bareOsSyscall: rename: source not found')
}
const buf = await vfs.readFile(from)
const u8 =
buf instanceof Uint8Array
? buf
: buf != null
? b4a.from(buf)
: b4a.alloc(0)
await vfs.writeFile(to, u8)
await vfs.unlink(from)
return { ok: true, from, to }
}
if (name === 'link') {
const strict =
shellEnv.BARE_OS_VFS_STRICT_HARDLINK === '1' ||
shellEnv.BARE_OS_VFS_STRICT_HARDLINK === 'true'
if (strict) {
const err = /** @type {Error & { code?: string }} */ (
new Error(
'EOPNOTSUPP: hard links are not supported on Hyperdrive (unset BARE_OS_VFS_STRICT_HARDLINK for legacy copy semantics)'
)
)
err.code = 'EOPNOTSUPP'
throw err
}
const existing = String(args.existing || args.path || '').trim()
const newPath = String(args.newPath || args.to || '').trim()
if (!existing.startsWith('/') || !newPath.startsWith('/')) {
throw new Error('bareOsSyscall: link requires absolute paths')
}
if (!(await vfs.exists(existing))) {
throw new Error('bareOsSyscall: link: source not found')
}
const buf = await vfs.readFile(existing)
const u8 =
buf instanceof Uint8Array
? buf
: buf != null
? b4a.from(buf)
: b4a.alloc(0)
await vfs.writeFile(newPath, u8)
return {
ok: true,
existing,
newPath,
note: 'Data copied; Hyperdrive has no shared inode hard links.'
}
}
if (name === 'access') {
const p = String(args.path || '').trim()
if (!p.startsWith('/'))
throw new Error('bareOsSyscall: absolute path required')
const ok = await vfs.exists(p)
if (!ok) throw new Error('ENOENT: access ' + p)
return { ok: true, path: p }
}
if (name === 'utimes') {
const p = String(args.path || '').trim()
if (!p.startsWith('/'))
throw new Error('bareOsSyscall: absolute path required')
const mtimeMs = Number(args.mtimeMs ?? args.mtime)
if (!Number.isFinite(mtimeMs)) {
throw new Error('bareOsSyscall: utimes requires numeric mtimeMs')
}
let cur = await vfs.readFile(p)
const u8 =
cur instanceof Uint8Array
? cur
: cur != null
? b4a.from(cur)
: b4a.alloc(0)
await vfs.writeFile(p, u8, { mtimeMs, bumpMtime: true })
return { ok: true, path: p, mtimeMs }
}
if (name === 'truncate' || name === 'ftruncate') {
const p = String(args.path || '').trim()
if (!p.startsWith('/'))
throw new Error('bareOsSyscall: absolute path required')
const len = Number(args.length ?? args.len ?? 0)
if (!Number.isFinite(len) || len < 0) {
throw new Error(
'bareOsSyscall: truncate length must be a non-negative number'
)
}
let cur = await vfs.readFile(p)
let u8 =
cur instanceof Uint8Array
? cur
: cur != null
? b4a.from(cur)
: b4a.alloc(0)
if (u8.byteLength > len) {
u8 = u8.subarray(0, len)
} else if (u8.byteLength < len) {
u8 = b4a.concat([u8, b4a.alloc(len - u8.byteLength)])
}
await vfs.writeFile(p, u8)
return { ok: true, path: p, length: len }
}
if (name === 'mount') {
const label = String(args.label || '').trim()
const source = String(args.source || '').trim()
if (!/^[a-zA-Z0-9][a-zA-Z0-9._-]{0,62}$/.test(label)) {
throw new Error('bareOsSyscall: mount label is invalid')
}
if (hdmsController.byLabel.has(label)) {
throw new Error('bareOsSyscall: mount label already mounted')
}
if (source === 'local') {
await hdmsController.create(this, label)
} else if (source) {
await hdmsController.addReadonly(this, label, source)
} else {
throw new Error('bareOsSyscall: mount source is required')
}
await persistBareOsMountsSnapshot(vfs, hdmsController, {
console: this.console
})
return { ok: true, label, source }
}
if (name === 'umount') {
const label = String(args.label || '').trim()
const force = !!args.force
if (!/^[a-zA-Z0-9][a-zA-Z0-9._-]{0,62}$/.test(label)) {
throw new Error('bareOsSyscall: umount label is invalid')
}
if (!force && typeof vfs.readdir === 'function') {
try {
const mntPath = `/mnt/${label}`
const list = await vfs.readdir(mntPath)
if (
Array.isArray(list) &&
list.some((n) => n && n !== '.' && n !== '..')
) {
throw new Error(
'umount: target is busy (use umount -f or remove files under ' +
mntPath +
')'
)
}
} catch (e) {
const msg = e?.message || String(e)
if (msg.includes('busy')) throw e
/* ENOENT → not mounted at VFS layer; still try HDMS remove */
}
}
await hdmsController.remove(this, label)
await persistBareOsMountsSnapshot(vfs, hdmsController, {
console: this.console
})
return { ok: true, label }
}
if (name === 'fsync' || name === 'fdatasync') {
const p = String(args.path || '').trim()
if (!p.startsWith('/')) {
throw new Error('bareOsSyscall: absolute path required')
}
if (typeof vfs.fsync === 'function') await vfs.fsync(p)
return {
ok: true,
path: p,
op: name,
note: 'Best-effort no-op unless the VFS exposes a flush primitive.'
}
}
if (name === 'posix_fadvise') {
const p = String(args.path || '').trim()
if (!p.startsWith('/')) {
throw new Error(
'bareOsSyscall: posix_fadvise: absolute path required'
)
}
return {
ok: true,
path: p,
op: name,
offset: Number(args.offset ?? 0) || 0,
len: Number(args.len ?? args.length ?? 0) || 0,
advice: Number(args.advice ?? args.adv ?? 0) || 0,
posixAlignment: 'simulated',
note: 'posix_fadvise is a logical no-op; documents Issue 7-style advice without host page-cache control.'
}
}
if (name === 'fcntl') {
const O_ACCMODE = 3
const O_APPEND = 0x400
const O_NONBLOCK = 0x800
const SETFL_MASK = O_APPEND | O_NONBLOCK
const fd = Number(args.fd)
if (!Number.isFinite(fd) || fd < 0 || fd > 65535) {
throw new Error('bareOsSyscall: fcntl: invalid fd')
}
const k = String(fd >>> 0)
const target = this.bareOsLogicalFds[k]
const isStd = k === '0' || k === '1' || k === '2'
if (!isStd && (target == null || target === '')) {
const err = /** @type {Error & { code?: string }} */ (
new Error('EBADF: fcntl')
)
err.code = 'EBADF'
throw err
}
const baseAcc = () => {
if (k === '0') return 0
if (k === '1' || k === '2') return 2
const s = String(target || '')
if (/posix-pipe:\d+:r$/.test(s)) return 0
if (/posix-pipe:\d+:w$/.test(s)) return 1
return 2
}
let cmd = args.cmd
if (typeof cmd === 'string') {
const u = String(cmd).toUpperCase()
if (u === 'F_GETLK' || u === 'F_SETLK' || u === 'F_SETLKW') {
const lockPath = bareOsResolveCooperativeLockPath(args, this)
if (!lockPath) {
return {
ok: false,
op: 'fcntl',
cmd: u,
code: 'EINVAL',
errnoHint: 'EINVAL',
cooperative: true,
note: 'Cooperative advisory locks require args.path (absolute) or args.fd whose logical target is an absolute path.'
}
}
if (
!this.bareOsCooperativeLocks ||
typeof this.bareOsCooperativeLocks !== 'object'
) {
this.bareOsCooperativeLocks = Object.create(null)
}
const locks =
/** @type {Record<string, { readers: Set<string>; writer: string | null }>} */ (
this.bareOsCooperativeLocks
)
const owner = String(
(this.env && this.env.BARE_OS_SESSION_ID) || 'session'
)
const flock =
args.flock && typeof args.flock === 'object'
? /** @type {Record<string, unknown>} */ (args.flock)
: {}
let lType = flock.l_type ?? flock.lType ?? args.l_type ?? args.lType
if (typeof lType === 'string') {
const up = String(lType).toUpperCase()
if (up === 'F_RDLCK' || up === 'RDLCK') lType = 0
else if (up === 'F_WRLCK' || up === 'WRLCK') lType = 1
else if (up === 'F_UNLCK' || up === 'UNLCK') lType = 2
}
lType = Number(lType)
if (!Number.isFinite(lType)) lType = 0
function ensureSlot(path) {
let s = locks[path]
if (!s) {
s = { readers: new Set(), writer: null }
locks[path] = s
}
return s
}
if (u === 'F_GETLK') {
const slot = locks[lockPath]
if (!slot || (!slot.writer && slot.readers.size === 0)) {
return {
ok: true,
op: 'fcntl',
cmd: 'F_GETLK',
path: lockPath,
cooperative: true,
flock: { l_type: 'F_UNLCK' }
}
}
if (slot.writer && slot.writer !== owner) {
return {
ok: true,
op: 'fcntl',
cmd: 'F_GETLK',
path: lockPath,
cooperative: true,
flock: { l_type: 'F_WRLCK', l_pid: 1 }
}
}
for (const sid of slot.readers) {
if (sid !== owner && lType === 1) {
return {
ok: true,
op: 'fcntl',
cmd: 'F_GETLK',
path: lockPath,
cooperative: true,
flock: { l_type: 'F_RDLCK', l_pid: 1 }
}
}
}
return {
ok: true,
op: 'fcntl',
cmd: 'F_GETLK',
path: lockPath,
cooperative: true,
flock: { l_type: 'F_UNLCK' }
}
}
const slot = ensureSlot(lockPath)
if (lType === 2) {
if (slot.writer === owner) slot.writer = null
slot.readers.delete(owner)
bareOsDrainFcntlLockWaiters(this, lockPath)
return {
ok: true,
op: 'fcntl',
cmd: u,
path: lockPath,
cooperative: true,
unlocked: true
}
}
const wantWrite = lType === 1
if (bareOsTryAcquireCooperativeLock(slot, owner, wantWrite)) {
return {
ok: true,
op: 'fcntl',
cmd: u,
path: lockPath,
cooperative: true,
locked: wantWrite ? 'write' : 'read'
}
}
const blocking =
u === 'F_SETLKW' && wantPosixFcntlBlockingWait(this.env)
if (!blocking) {
return {
ok: false,
op: 'fcntl',
cmd: u,
code: 'EAGAIN',
errnoHint: 'EAGAIN',
cooperative: true,
note: wantWrite
? 'Write lock held by another session.'
: 'Read or write lock held by another session.'
}
}
const maxMs = bareOsFcntlLockWaitMsMax(this.env)
return await new Promise((resolve) => {
const entry = {
resolve,
timer: /** @type {ReturnType<typeof setTimeout> | null} */ (
null
),
owner,
wantWrite
}
entry.timer = setTimeout(() => {
bareOsRemoveFcntlWaiter(this, lockPath, entry)
resolve({
ok: false,
op: 'fcntl',
cmd: 'F_SETLKW',
code: 'ETIMEDOUT',
errnoHint: 'ETIMEDOUT',
cooperative: true,
note: 'F_SETLKW wait exceeded BARE_OS_FCNTL_LOCK_WAIT_MS_MAX (or default 30000).'
})
}, maxMs)
if (
!this.bareOsFcntlLockWaitQueues ||
typeof this.bareOsFcntlLockWaitQueues !== 'object'
) {
this.bareOsFcntlLockWaitQueues = Object.create(null)
}
const wq = this.bareOsFcntlLockWaitQueues
if (!Array.isArray(wq[lockPath])) wq[lockPath] = []
wq[lockPath].push(entry)
})
}
if (u === 'F_GETFL' || u === 'GETFL') cmd = 3
else if (u === 'F_SETFL' || u === 'SETFL') cmd = 4
else {
const err = /** @type {Error & { code?: string }} */ (
new Error('ENOTSUP: fcntl cmd')
)
err.code = 'ENOTSUP'
throw err
}
}
cmd = Number(cmd)
if (cmd === 3) {
const acc = baseAcc() & O_ACCMODE
const extra = (this.bareOsLogicalFdFlags[k] || 0) & SETFL_MASK
return {
ok: true,
op: 'fcntl',
cmd: 'F_GETFL',
fd,
flags: acc | extra
}
}
if (cmd === 4) {
if (
!this.bareOsLogicalFdFlags ||
typeof this.bareOsLogicalFdFlags !== 'object'
) {
this.bareOsLogicalFdFlags = {}
}
const arg = Number(args.arg ?? args.value ?? 0) >>> 0
this.bareOsLogicalFdFlags[k] = arg & SETFL_MASK
const acc = baseAcc() & O_ACCMODE
const extra = this.bareOsLogicalFdFlags[k] & SETFL_MASK
return {
ok: true,
op: 'fcntl',
cmd: 'F_SETFL',
fd,
flags: acc | extra
}
}
const err2 = /** @type {Error & { code?: string }} */ (
new Error('ENOTSUP: fcntl cmd')
)
err2.code = 'ENOTSUP'
throw err2
}
if (name === 'select') {
const readfds = Array.isArray(args.readfds) ? args.readfds : []
const writefds = Array.isArray(args.writefds) ? args.writefds : []
/** @type {Map<number, string>} */
const ev = new Map()
for (const x of readfds) {
const fd = Number(x)
if (Number.isFinite(fd)) ev.set(fd, (ev.get(fd) || '') + 'r')
}
for (const x of writefds) {
const fd = Number(x)
if (Number.isFinite(fd)) ev.set(fd, (ev.get(fd) || '') + 'w')
}
const fds = [...ev.entries()].map(([fd, events]) => ({ fd, events }))
const timeoutMs = Math.max(
0,
Math.min(60000, Math.floor(Number(args.timeoutMs) || 0))
)
const pr =
timeoutMs > 0
? await this.bareOsPosixPoll({ fds, timeoutMs })
: this.bareOsPosixPollProbe({ fds })
const readSet = new Set(
readfds.map((x) => Number(x)).filter(Number.isFinite)
)
const writeSet = new Set(
writefds.map((x) => Number(x)).filter(Number.isFinite)
)
const readReady = []
const writeReady = []
for (const row of pr.ready || []) {
const fd = row.fd
const rv = String(row.revents || '')
if (readSet.has(fd) && rv.includes('r')) readReady.push(fd)
if (writeSet.has(fd) && rv.includes('w')) writeReady.push(fd)
}
return {
ok: true,
op: 'select',
readReady,
writeReady,
timedOut: pr.timedOut === true,
waitedMs: pr.waitedMs ?? 0,
pollClock: pr.pollClock
}
}
if (name === 'umask') {
const env = this.vfs?.env
if (!env || typeof env !== 'object') {
throw new Error('bareOsSyscall: umask: vfs.env unavailable')
}
const curRaw = String(env.UMASK || '022').trim()
const curMask = Number.parseInt(curRaw, 8)
const validCur =
Number.isFinite(curMask) && curMask >= 0 && curMask <= 0o777
if (
args.set !== undefined &&
args.set !== null &&
String(args.set).trim() !== ''
) {
const next = Number.parseInt(String(args.set), 8)
if (!Number.isFinite(next) || next < 0 || next > 0o777) {
throw new Error('bareOsSyscall: umask: invalid mask')
}
env.UMASK = next.toString(8)
}
const nowMask = Number.parseInt(String(env.UMASK || '022'), 8)
return {
ok: true,
op: 'umask',
previous: validCur ? curMask : 0o022,
current: Number.isFinite(nowMask) ? nowMask : 0o022
}
}
if (name === 'readv') {
const fd = Number(args.fd)
if (!Number.isFinite(fd)) {
throw new Error('bareOsSyscall: readv: fd is required')
}
const iovs = Array.isArray(args.iovs) ? args.iovs : []
const maxIovs = 16
const maxPer = 65536
const maxTotal = 1024 * 1024
let totalBytes = 0
/** @type {string[]} */
const buffers = []
for (const iov of iovs.slice(0, maxIovs)) {
const want = Math.min(
maxPer,
Math.max(
0,
Math.floor(
Number(
iov && typeof iov === 'object' && 'length' in iov
? iov.length
: iov && typeof iov === 'object' && 'maxBytes' in iov
? iov.maxBytes
: 4096
)
)
)
)
if (want <= 0) {
buffers.push('')
continue
}
if (totalBytes + want > maxTotal) {
throw new Error('bareOsSyscall: readv: total iov budget exceeded')
}
const r = this.bareOsPosixFdSimRead(fd, want)
if (!r || !r.ok) {
if (r && r.reason === 'EAGAIN') {
const err = /** @type {Error & { code?: string }} */ (
new Error('EAGAIN: readv')
)
err.code = 'EAGAIN'
throw err
}
throw new Error(
'bareOsSyscall: readv: ' + (r && r.reason ? r.reason : 'failed')
)
}
const chunk = String(r.data || '')
buffers.push(chunk)
totalBytes += b4a.from(chunk, 'utf8').byteLength
if (chunk.length < want) break
}
return { ok: true, op: 'readv', fd, buffers, bytesRead: totalBytes }
}
if (name === 'writev') {
const fd = Number(args.fd)
if (!Number.isFinite(fd)) {
throw new Error('bareOsSyscall: writev: fd is required')
}
const bufs = Array.isArray(args.buffers) ? args.buffers : []
const maxChunks = 64
const maxTotal = 1024 * 1024
const pieces = []
let n = 0
for (const b of bufs.slice(0, maxChunks)) {
const s =
typeof b === 'string'
? b
: b instanceof Uint8Array
? b4a.toString(b, 'utf8')
: String(b)
const add = b4a.from(s, 'utf8').byteLength
n += add
if (n > maxTotal) {
throw new Error('bareOsSyscall: writev: total length exceeded')
}
pieces.push(s)
}
const payload = pieces.join('')
const w = this.bareOsPosixFdSimWrite(fd, payload)
if (!w || !w.ok) {
if (w && w.reason === 'EAGAIN') {
const err = /** @type {Error & { code?: string }} */ (
new Error('EAGAIN: writev')
)
err.code = 'EAGAIN'
throw err
}
throw new Error(
'bareOsSyscall: writev: ' + (w && w.reason ? w.reason : 'failed')
)
}
return {
ok: true,
op: 'writev',
fd,
bytesWritten: w.bytes ?? b4a.from(payload, 'utf8').byteLength
}
}
if (name === 'getsockopt' || name === 'setsockopt') {
const SOL_SOCKET = 1
const SO_KEEPALIVE = 9
const SO_SNDBUF = 7
const SO_RCVBUF = 8
const IPPROTO_TCP = 6
const TCP_NODELAY = 1
const DEF_SNDBUF = 262144
const DEF_RCVBUF = 262144
const fd = Number(args.fd)
if (
wantPosixSocketFdBridge(shellEnv) &&
Number.isFinite(fd) &&
this.bareOsSocketBridgeByFd &&
typeof this.bareOsSocketBridgeByFd === 'object'
) {
const k = String(fd >>> 0)
const slot = this.bareOsSocketBridgeByFd[k]
if (slot && typeof slot === 'object') {
if (!slot.bridgeSockOpts) {
slot.bridgeSockOpts = {
soKeepalive: 1,
tcpNodelay: 0,
sndbuf: DEF_SNDBUF,
rcvbuf: DEF_RCVBUF
}
} else {
if (slot.bridgeSockOpts.sndbuf == null)
slot.bridgeSockOpts.sndbuf = DEF_SNDBUF
if (slot.bridgeSockOpts.rcvbuf == null)
slot.bridgeSockOpts.rcvbuf = DEF_RCVBUF
}
const level = Number(args.level) >>> 0
const optname = Number(args.optname) >>> 0
if (name === 'getsockopt') {
let intVal = 0
if (level === SOL_SOCKET && optname === SO_KEEPALIVE) {
intVal = slot.bridgeSockOpts.soKeepalive ? 1 : 0
} else if (level === SOL_SOCKET && optname === SO_SNDBUF) {
intVal = Math.max(
4096,
Number(slot.bridgeSockOpts.sndbuf) || DEF_SNDBUF
)
} else if (level === SOL_SOCKET && optname === SO_RCVBUF) {
intVal = Math.max(
4096,
Number(slot.bridgeSockOpts.rcvbuf) || DEF_RCVBUF
)
} else if (level === IPPROTO_TCP && optname === TCP_NODELAY) {
intVal = slot.bridgeSockOpts.tcpNodelay ? 1 : 0
} else {
return {
ok: false,
op: name,
fd,
code: 'ENOPROTOOPT',
errnoHint: 'ENOPROTOOPT',
posixAlignment: 'partial',
note: 'Bridge supports SOL_SOCKET/SO_KEEPALIVE, SO_SNDBUF, SO_RCVBUF, and IPPROTO_TCP/TCP_NODELAY.'
}
}
return {
ok: true,
op: name,
fd,
level,
optname,
intValue: intVal,
posixAlignment: 'partial',
note: 'Logical socket-bridge option read; does not change host UDX/TCP stack.'
}
}
const v = Number(args.value ?? args.optval ?? args.intValue ?? 0)
const on = Number.isFinite(v) && v !== 0 ? 1 : 0
if (level === SOL_SOCKET && optname === SO_KEEPALIVE) {
slot.bridgeSockOpts.soKeepalive = on
} else if (level === IPPROTO_TCP && optname === TCP_NODELAY) {
slot.bridgeSockOpts.tcpNodelay = on
} else if (level === SOL_SOCKET && optname === SO_SNDBUF) {
const n = Math.floor(Number(v))
if (!Number.isFinite(n) || n < 4096) {
return {
ok: false,
op: name,
fd,
code: 'EINVAL',
errnoHint: 'EINVAL',
posixAlignment: 'partial',
note: 'SO_SNDBUF must be >= 4096'
}
}
slot.bridgeSockOpts.sndbuf = Math.min(n, 4 * 1024 * 1024)
} else if (level === SOL_SOCKET && optname === SO_RCVBUF) {
const n = Math.floor(Number(v))
if (!Number.isFinite(n) || n < 4096) {
return {
ok: false,
op: name,
fd,
code: 'EINVAL',
errnoHint: 'EINVAL',
posixAlignment: 'partial',
note: 'SO_RCVBUF must be >= 4096'
}
}
slot.bridgeSockOpts.rcvbuf = Math.min(n, 4 * 1024 * 1024)
} else {
return {
ok: false,
op: name,
fd,
code: 'ENOPROTOOPT',
errnoHint: 'ENOPROTOOPT',
posixAlignment: 'partial',
note: 'Bridge setsockopt: SOL_SOCKET/SO_KEEPALIVE, SO_SNDBUF, SO_RCVBUF, and IPPROTO_TCP/TCP_NODELAY.'
}
}
return {
ok: true,
op: name,
fd,
level,
optname,
posixAlignment: 'partial'
}
}
}
return {
ok: false,
op: name,
code: 'ENOSYS',
errnoHint: 'ENOSYS',
posixAlignment: 'ENOTSUP',
note: 'getsockopt/setsockopt need BARE_OS_POSIX_SOCKET_FD_BRIDGE and a bridge socket fd; else ENOSYS.'
}
}
if (
name === 'socket' ||
name === 'bind' ||
name === 'listen' ||
name === 'accept' ||
name === 'connect' ||
name === 'send' ||
name === 'sendmsg' ||
name === 'recv' ||
name === 'recvfrom' ||
name === 'recvmsg' ||
name === 'shutdown'
) {
if (wantPosixSocketFdBridge(shellEnv)) {
const st = this.bareOsPosixFdSimState
const bridgeNotify = (slot, kind) => {
const key = kind === 'accept' ? 'acceptWaiters' : 'recvWaiters'
const waiters = slot && Array.isArray(slot[key]) ? slot[key] : null
if (!waiters || waiters.length === 0) return
const pending = waiters.splice(0, waiters.length)
for (const resolve of pending) {
try {
resolve()
} catch {
/* ignore */
}
}
}
const bridgeWait = (slot, timeoutMs, kind) =>
new Promise((resolve) => {
const key = kind === 'accept' ? 'acceptWaiters' : 'recvWaiters'
if (!slot[key] || !Array.isArray(slot[key])) slot[key] = []
let settled = false
const done = () => {
if (settled) return
settled = true
const waiters = slot[key]
if (Array.isArray(waiters)) {
const idx = waiters.indexOf(done)
if (idx >= 0) waiters.splice(idx, 1)
}
resolve()
}
slot[key].push(done)
const ms = Math.max(1, Math.min(100, Number(timeoutMs) || 10))
setTimeout(done, ms)
})
if (name === 'socket') {
const fd = st.nextFd++
const k = String(fd)
if (
!this.bareOsSocketBridgeByFd ||
typeof this.bareOsSocketBridgeByFd !== 'object'
) {
this.bareOsSocketBridgeByFd = Object.create(null)
}
const sockType = Number(args.type ?? args.socktype ?? 1) >>> 0
this.bareOsSocketBridgeByFd[k] = {
domain: Number(args.domain ?? args.af ?? 2),
type: sockType,
sockType,
state: 'created',
recvWaiters: [],
acceptWaiters: [],
aliasKeys: new Set([k])
}
this.bareOsRegisterLogicalFd(fd, 'bare-os-socket-bridge:idle')
return {
ok: true,
op: name,
fd,
bridge: true,
posixAlignment: 'partial',
note:
sockType === 2
? 'Optional POSIX socket FD bridge (SOCK_DGRAM): bind(port,host) for passive UDP, or connect(port,host) for connected mode, when ctx.bare exposes bareDgram.Socket (UDX).'
: 'Optional POSIX socket FD bridge (SOCK_STREAM): follow with connect(port, host) when ctx.bare exposes bareTcp.Socket.'
}
}
if (name === 'connect') {
const fd = Number(args.fd)
if (!Number.isFinite(fd)) {
throw new Error('bareOsSyscall: connect: fd required')
}
const k = String(fd >>> 0)
const slot =
this.bareOsSocketBridgeByFd &&
typeof this.bareOsSocketBridgeByFd === 'object'
? this.bareOsSocketBridgeByFd[k]
: null
const port = Number(args.port)
const host = String(args.host || args.address || '127.0.0.1').trim()
if (!Number.isFinite(port) || port <= 0 || port > 65535) {
return {
ok: false,
op: name,
code: 'EINVAL',
errnoHint: 'EINVAL'
}
}
if (!slot) {
return {
ok: false,
op: name,
code: 'EINVAL',
errnoHint: 'EINVAL',
note: 'connect: unknown bridge fd.'
}
}
const isDgram = Number(slot.sockType ?? slot.type ?? 1) === 2
const dgramBound =
isDgram && slot.state === 'udp_bound' && slot.sock
if (!isDgram && slot.state !== 'created') {
return {
ok: false,
op: name,
code: 'ENOTSUP',
errnoHint: 'ENOTSUP',
posixAlignment: 'ENOTSUP',
note: 'connect: requires prior bridge socket(2) fd in created state.'
}
}
if (isDgram && slot.state !== 'created' && !dgramBound) {
return {
ok: false,
op: name,
code: 'EINVAL',
errnoHint: 'EINVAL',
posixAlignment: 'ENOTSUP',
note: 'connect: SOCK_DGRAM bridge requires created socket or bind(2) before connect on the same fd.'
}
}
const Tcp =
bareLibrary.bareTcp &&
bareLibrary.bareTcp.Socket &&
typeof bareLibrary.bareTcp.Socket === 'function'
? bareLibrary.bareTcp.Socket
: null
const dg = bareLibrary.bareDgram
const UdpSocket =
dg &&
((typeof dg.Socket === 'function' && dg.Socket) ||
(dg.default &&
typeof dg.default.Socket === 'function' &&
dg.default.Socket))
? dg.Socket || dg.default.Socket
: null
if (isDgram) {
if (!UdpSocket) {
return {
ok: false,
op: name,
code: 'ENOTSUP',
errnoHint: 'ENOTSUP',
posixAlignment: 'ENOTSUP',
note: 'SOCK_DGRAM bridge requires bareDgram.Socket merged in ctx.bare (bare-dgram / UDX).'
}
}
if (dgramBound) {
const usock = /** @type {{ connect: Function }} */ (slot.sock)
await new Promise((resolve, reject) => {
try {
usock.connect(port, host || '127.0.0.1', (err) =>
err ? reject(err) : resolve(undefined)
)
} catch (e) {
reject(e)
}
})
slot.state = 'connected'
this.bareOsLogicalFds[k] =
'bare-os-socket-bridge:udp:' +
(host || '127.0.0.1') +
':' +
port
return {
ok: true,
op: name,
fd,
bridge: true,
posixAlignment: 'partial',
transport: 'udp'
}
}
const usock = new UdpSocket({})
await new Promise((resolve, reject) => {
try {
usock.connect(port, host || '127.0.0.1', (err) =>
err ? reject(err) : resolve(undefined)
)
} catch (e) {
reject(e)
}
})
slot.state = 'connected'
slot.sock = usock
slot.transport = 'udp'
slot.dgramRecvQueue = []
slot.dgramRecvDropped = 0
const qmax = bareOsPosixDgramRecvQueueMax(shellEnv)
usock.on('message', (message, rinfo) => {
let u8 =
message instanceof Uint8Array ? message : b4a.from(message)
if (u8.byteLength > 1024 * 1024) u8 = u8.slice(0, 1024 * 1024)
if (slot.dgramRecvQueue.length >= qmax) {
slot.dgramRecvDropped = (slot.dgramRecvDropped || 0) + 1
return
}
slot.dgramRecvQueue.push({
buf: u8.slice(),
rinfo:
rinfo && typeof rinfo === 'object'
? {
address: String(rinfo.address || ''),
port: Number(rinfo.port) >>> 0
}
: null
})
bridgeNotify(slot, 'recv')
})
this.bareOsLogicalFds[k] =
'bare-os-socket-bridge:udp:' +
(host || '127.0.0.1') +
':' +
port
return {
ok: true,
op: name,
fd,
bridge: true,
posixAlignment: 'partial',
transport: 'udp'
}
}
if (!Tcp) {
return {
ok: false,
op: name,
code: 'ENOTSUP',
errnoHint: 'ENOTSUP',
posixAlignment: 'ENOTSUP',
note: 'connect: SOCK_STREAM bridge requires bareTcp.Socket merged in ctx.bare (bare-tcp bundle).'
}
}
const connectHosts =
host === 'localhost'
? ['127.0.0.1', '::1']
: [host || '127.0.0.1']
/** @type {unknown[]} */
const connectErrors = []
/** @type {any} */
let sock = null
const connectTimeoutMs = Math.min(
300000,
Math.max(
0,
Number.parseInt(
String(
shellEnv.BARE_OS_POSIX_SOCKET_CONNECT_TIMEOUT_MS || '0'
).trim(),
10
) || 0
)
)
try {
for (const candHost of connectHosts) {
const candSock = new Tcp()
const connectPromise = new Promise((resolve, reject) => {
try {
candSock.connect(
port,
candHost || '127.0.0.1',
{},
(err) => (err ? reject(err) : resolve(undefined))
)
} catch (e) {
reject(e)
}
})
try {
if (connectTimeoutMs > 0) {
/** @type {ReturnType<typeof setTimeout> | null} */
let to = null
const timeoutP = new Promise((_, reject) => {
to = setTimeout(
() =>
reject(
Object.assign(new Error('ETIMEDOUT'), {
code: 'ETIMEDOUT'
})
),
connectTimeoutMs
)
})
await Promise.race([connectPromise, timeoutP]).finally(
() => {
if (to) clearTimeout(to)
}
)
} else {
await connectPromise
}
sock = candSock
break
} catch (e) {
connectErrors.push(e)
try {
candSock.destroy?.()
} catch {
/* ignore */
}
}
}
} catch (e) {
try {
sock.destroy?.()
} catch {
/* ignore */
}
if (
e &&
/** @type {{ code?: string }} */ (e).code === 'ETIMEDOUT'
) {
return {
ok: false,
op: name,
code: 'ETIMEDOUT',
errnoHint: 'ETIMEDOUT',
posixAlignment: 'partial',
note: `connect: timed out after ${connectTimeoutMs}ms (BARE_OS_POSIX_SOCKET_CONNECT_TIMEOUT_MS).`
}
}
throw e
}
if (!sock) {
const first = connectErrors[0]
if (
first &&
/** @type {{ code?: string }} */ (first).code === 'ETIMEDOUT'
) {
return {
ok: false,
op: name,
code: 'ETIMEDOUT',
errnoHint: 'ETIMEDOUT',
posixAlignment: 'partial',
note: `connect: timed out after ${connectTimeoutMs}ms (BARE_OS_POSIX_SOCKET_CONNECT_TIMEOUT_MS).`
}
}
return {
ok: false,
op: name,
code: 'ECONNREFUSED',
errnoHint: 'ECONNREFUSED',
posixAlignment: 'partial',
note: `connect: all address candidates failed for host=${host || '127.0.0.1'}`
}
}
slot.state = 'connected'
slot.sock = sock
slot.transport = 'tcp'
slot.tcpRecvQueue = []
slot.tcpRecvDropped = 0
const tcpQmax = bareOsPosixDgramRecvQueueMax(shellEnv)
sock.on('data', (message) => {
let u8 =
message instanceof Uint8Array ? message : b4a.from(message)
if (u8.byteLength > 1024 * 1024) u8 = u8.slice(0, 1024 * 1024)
if (!Array.isArray(slot.tcpRecvQueue)) slot.tcpRecvQueue = []
if (slot.tcpRecvQueue.length >= tcpQmax) {
slot.tcpRecvDropped = (slot.tcpRecvDropped || 0) + 1
return
}
slot.tcpRecvQueue.push({ buf: u8.slice() })
bridgeNotify(slot, 'recv')
})
sock.on('end', () => {
slot.tcpPeerEnded = true
bridgeNotify(slot, 'recv')
})
sock.on('error', () => {
slot.tcpSockError = true
bridgeNotify(slot, 'recv')
})
this.bareOsLogicalFds[k] =
'bare-os-socket-bridge:tcp:' + (host || '127.0.0.1') + ':' + port
return {
ok: true,
op: name,
fd,
bridge: true,
posixAlignment: 'partial',
transport: 'tcp'
}
}
if (name === 'bind') {
const fd = Number(args.fd)
if (!Number.isFinite(fd)) {
return {
ok: false,
op: name,
code: 'EINVAL',
errnoHint: 'EINVAL',
note: 'bind: fd required'
}
}
const k = String(fd >>> 0)
const slot =
this.bareOsSocketBridgeByFd &&
typeof this.bareOsSocketBridgeByFd === 'object'
? this.bareOsSocketBridgeByFd[k]
: null
const isDgram = Number(slot?.sockType ?? slot?.type ?? 1) === 2
if (isDgram) {
if (!slot || slot.state !== 'created') {
return {
ok: false,
op: name,
code: 'EINVAL',
errnoHint: 'EINVAL',
note: 'bind: requires SOCK_DGRAM bridge socket in created state.'
}
}
const dg = bareLibrary.bareDgram
const UdpSocket =
dg &&
((typeof dg.Socket === 'function' && dg.Socket) ||
(dg.default &&
typeof dg.default.Socket === 'function' &&
dg.default.Socket))
? dg.Socket || dg.default.Socket
: null
if (!UdpSocket) {
return {
ok: false,
op: name,
code: 'ENOTSUP',
errnoHint: 'ENOTSUP',
posixAlignment: 'ENOTSUP',
note: 'bind: SOCK_DGRAM bridge requires bareDgram.Socket merged in ctx.bare (bare-dgram / UDX).'
}
}
const port = Number(args.port)
const host =
String(args.host || args.address || '0.0.0.0').trim() ||
'0.0.0.0'
if (!Number.isFinite(port) || port < 0 || port > 65535) {
return {
ok: false,
op: name,
code: 'EINVAL',
errnoHint: 'EINVAL',
note: 'bind: port required (065535)'
}
}
const bindUdpOnce = (sock, bindPort) =>
new Promise((resolve, reject) => {
const tmo = setTimeout(() => {
sock.removeAllListeners('error')
reject(new Error('bind: timeout'))
}, 30000)
sock.once('error', (e) => {
clearTimeout(tmo)
sock.removeAllListeners('error')
reject(e)
})
try {
sock.bind(bindPort, host, () => {
clearTimeout(tmo)
sock.removeAllListeners('error')
resolve(undefined)
})
} catch (e) {
clearTimeout(tmo)
reject(e)
}
})
let usock = new UdpSocket({})
try {
await bindUdpOnce(usock, port)
} catch (e) {
try {
await usock.close?.()
} catch (_) {}
if (
!bindFallbackEnabled(shellEnv) ||
!isAddrInUse(e) ||
port === 0
) {
return {
ok: false,
op: name,
code: 'EADDRINUSE',
errnoHint: 'EADDRINUSE',
posixAlignment: 'partial',
note: String(
e && /** @type {Error} */ (e).message ? e.message : e
)
}
}
usock = new UdpSocket({})
try {
await bindUdpOnce(usock, 0)
} catch (e2) {
try {
await usock.close?.()
} catch (_) {}
return {
ok: false,
op: name,
code: 'EADDRINUSE',
errnoHint: 'EADDRINUSE',
posixAlignment: 'partial',
note: String(
e2 && /** @type {Error} */ (e2).message ? e2.message : e2
)
}
}
}
let boundUdpPort = port
try {
const a =
usock.address && typeof usock.address === 'function'
? usock.address()
: null
if (a && typeof a === 'object' && Number.isFinite(a.port))
boundUdpPort = Number(a.port)
} catch (_) {}
slot.state = 'udp_bound'
slot.sock = usock
slot.transport = 'udp'
slot.bindPort = boundUdpPort
slot.bindHost = host
slot.dgramRecvQueue = []
slot.dgramRecvDropped = 0
const qmax = bareOsPosixDgramRecvQueueMax(shellEnv)
usock.on('message', (message, rinfo) => {
let u8 =
message instanceof Uint8Array ? message : b4a.from(message)
if (u8.byteLength > 1024 * 1024) u8 = u8.slice(0, 1024 * 1024)
if (slot.dgramRecvQueue.length >= qmax) {
slot.dgramRecvDropped = (slot.dgramRecvDropped || 0) + 1
return
}
slot.dgramRecvQueue.push({
buf: u8.slice(),
rinfo:
rinfo && typeof rinfo === 'object'
? {
address: String(rinfo.address || ''),
port: Number(rinfo.port) >>> 0
}
: null
})
bridgeNotify(slot, 'recv')
})
this.bareOsLogicalFds[k] =
'bare-os-socket-bridge:udp-bound:' + host + ':' + boundUdpPort
return {
ok: true,
op: name,
fd,
bridge: true,
posixAlignment: 'partial',
transport: 'udp',
port: boundUdpPort,
host
}
}
if (!slot || slot.state !== 'created') {
return {
ok: false,
op: name,
code: 'EINVAL',
errnoHint: 'EINVAL',
note: 'bind: requires SOCK_STREAM bridge socket in created state.'
}
}
const port = Number(args.port)
const host =
String(args.host || args.address || '0.0.0.0').trim() || '0.0.0.0'
if (!Number.isFinite(port) || port < 0 || port > 65535) {
return {
ok: false,
op: name,
code: 'EINVAL',
errnoHint: 'EINVAL',
note: 'bind: port required (065535)'
}
}
slot.bindPort = port
slot.bindHost = host
slot.state = 'bound'
this.bareOsLogicalFds[k] =
'bare-os-socket-bridge:tcp-bound:' + host + ':' + port
return {
ok: true,
op: name,
fd,
bridge: true,
posixAlignment: 'partial',
port,
host
}
}
if (name === 'listen') {
const fd = Number(args.fd)
if (!Number.isFinite(fd)) {
return {
ok: false,
op: name,
code: 'EINVAL',
errnoHint: 'EINVAL',
note: 'listen: fd required'
}
}
const k = String(fd >>> 0)
const slot =
this.bareOsSocketBridgeByFd &&
typeof this.bareOsSocketBridgeByFd === 'object'
? this.bareOsSocketBridgeByFd[k]
: null
if (!slot || slot.state !== 'bound') {
return {
ok: false,
op: name,
code: 'EINVAL',
errnoHint: 'EINVAL',
note: 'listen: call bind(2) on the bridge fd first.'
}
}
const TcpServer =
bareLibrary.bareTcp &&
bareLibrary.bareTcp.Server &&
typeof bareLibrary.bareTcp.Server === 'function'
? bareLibrary.bareTcp.Server
: null
if (!TcpServer) {
return {
ok: false,
op: name,
code: 'ENOTSUP',
errnoHint: 'ENOTSUP',
posixAlignment: 'ENOTSUP',
note: 'listen: bareTcp.Server not merged in ctx.bare (bare-tcp bundle).'
}
}
const backlog = Math.min(
511,
Math.max(1, Math.floor(Number(args.backlog) || 511))
)
const acceptQmax = bareOsPosixAcceptQueueMax(shellEnv)
const dgramQmax = bareOsPosixDgramRecvQueueMax(shellEnv)
const server = new TcpServer()
slot.acceptQueue = []
server.on('connection', (sock) => {
if (!Array.isArray(slot.acceptQueue)) return
if (slot.acceptQueue.length >= acceptQmax) {
try {
sock.destroy?.()
} catch (_) {}
return
}
const wrap = {
sock,
tcpRecvQueue: [],
tcpRecvDropped: 0
}
sock.on('data', (message) => {
let u8 =
message instanceof Uint8Array ? message : b4a.from(message)
if (u8.byteLength > 1024 * 1024) u8 = u8.slice(0, 1024 * 1024)
if (wrap.tcpRecvQueue.length >= dgramQmax) {
wrap.tcpRecvDropped = (wrap.tcpRecvDropped || 0) + 1
return
}
wrap.tcpRecvQueue.push({ buf: u8.slice() })
bridgeNotify(wrap, 'recv')
})
slot.acceptQueue.push(wrap)
bridgeNotify(slot, 'accept')
})
try {
await new Promise((resolve, reject) => {
server.once('error', reject)
server.once('listening', resolve)
server.listen(slot.bindPort, slot.bindHost, backlog)
})
} catch (e) {
if (
!bindFallbackEnabled(shellEnv) ||
!isAddrInUse(e) ||
slot.bindPort === 0
) {
return {
ok: false,
op: name,
code: 'EADDRINUSE',
errnoHint: 'EADDRINUSE',
posixAlignment: 'partial',
note: String(e && e.message ? e.message : e)
}
}
try {
await new Promise((resolve, reject) => {
server.once('error', reject)
server.once('listening', resolve)
server.listen(0, slot.bindHost, backlog)
})
} catch (e2) {
return {
ok: false,
op: name,
code: 'EADDRINUSE',
errnoHint: 'EADDRINUSE',
posixAlignment: 'partial',
note: String(e2 && e2.message ? e2.message : e2)
}
}
const ephem = readListeningTcpPort(server, 0)
if (Number.isFinite(ephem) && ephem > 0) slot.bindPort = ephem
}
slot.server = server
slot.state = 'listening'
this.bareOsLogicalFds[k] =
'bare-os-socket-bridge:tcp-listen:' +
slot.bindHost +
':' +
slot.bindPort
const addr =
server.address && typeof server.address === 'function'
? server.address()
: null
return {
ok: true,
op: name,
fd,
bridge: true,
posixAlignment: 'partial',
address: addr
}
}
if (name === 'accept') {
const fd = Number(args.fd)
if (!Number.isFinite(fd)) {
return {
ok: false,
op: name,
code: 'EINVAL',
errnoHint: 'EINVAL',
note: 'accept: listening fd required'
}
}
const k = String(fd >>> 0)
const slot =
this.bareOsSocketBridgeByFd &&
typeof this.bareOsSocketBridgeByFd === 'object'
? this.bareOsSocketBridgeByFd[k]
: null
if (!slot || slot.state !== 'listening') {
return {
ok: false,
op: name,
code: 'EINVAL',
errnoHint: 'EINVAL',
note: 'accept: fd must be a listening SOCK_STREAM bridge socket.'
}
}
const nonblock = (this.bareOsLogicalFdFlags[k] & 0x800) === 0x800
const blockMax = bareOsPosixAcceptBlockMsMax(shellEnv)
const t0 = Date.now()
while (true) {
const q = slot.acceptQueue
if (Array.isArray(q) && q.length > 0) {
const wrap = q.shift()
const sock = wrap && wrap.sock
if (!sock) continue
const st = this.bareOsPosixFdSimState
const newFd = st.nextFd++
const nk = String(newFd)
this.bareOsSocketBridgeByFd[nk] = {
state: 'connected',
sock,
transport: 'tcp',
sockType: 1,
type: 1,
tcpRecvQueue: wrap.tcpRecvQueue,
tcpRecvDropped: wrap.tcpRecvDropped || 0,
recvWaiters: wrap.recvWaiters || [],
aliasKeys: new Set([nk])
}
this.bareOsRegisterLogicalFd(
newFd,
'bare-os-socket-bridge:tcp-accepted'
)
return {
ok: true,
op: name,
fd: newFd,
bridge: true,
posixAlignment: 'partial',
listenFd: fd
}
}
if (nonblock) {
return {
ok: false,
op: name,
code: 'EAGAIN',
errnoHint: 'EAGAIN',
posixAlignment: 'EAGAIN',
bridge: true,
note: 'accept: no connection ready (O_NONBLOCK)'
}
}
if (Date.now() - t0 >= blockMax) {
return {
ok: false,
op: name,
code: 'EAGAIN',
errnoHint: 'EAGAIN',
posixAlignment: 'partial',
bridge: true,
note: 'accept: timed out waiting (same budget as BARE_OS_POSIX_DGRAM_RECV_BLOCK_MS_MAX)'
}
}
await bridgeWait(slot, blockMax - (Date.now() - t0), 'accept')
}
}
if (name === 'shutdown') {
const fd = Number(args.fd)
if (!Number.isFinite(fd)) {
return {
ok: false,
op: name,
code: 'EINVAL',
errnoHint: 'EINVAL',
note: 'shutdown: fd required'
}
}
return await bareOsShutdownSocketBridgeFd(
/** @type {Record<string, unknown>} */ (this),
fd,
args && typeof args === 'object'
? /** @type {Record<string, unknown>} */ (
args.how ??
/** @type {Record<string, unknown>} */ (args).shutdownHow
)
: undefined
)
}
if (
name === 'send' ||
name === 'sendmsg' ||
name === 'recv' ||
name === 'recvfrom' ||
name === 'recvmsg'
) {
const logicalOp = name
const fd = Number(args.fd)
if (!Number.isFinite(fd)) {
return {
ok: false,
op: logicalOp,
code: 'EINVAL',
errnoHint: 'EINVAL',
posixAlignment: 'ENOTSUP',
note: 'send/sendmsg/recv/recvfrom/recvmsg: fd required'
}
}
const k = String(fd >>> 0)
const slot =
this.bareOsSocketBridgeByFd &&
typeof this.bareOsSocketBridgeByFd === 'object'
? this.bareOsSocketBridgeByFd[k]
: null
const udpReady =
slot &&
slot.transport === 'udp' &&
slot.sock &&
(slot.state === 'connected' || slot.state === 'udp_bound')
const tcpReady =
slot &&
slot.transport === 'tcp' &&
slot.sock &&
slot.state === 'connected'
if (!udpReady && !tcpReady) {
return {
ok: false,
op: logicalOp,
code: 'ENOTSUP',
errnoHint: 'ENOTSUP',
posixAlignment: 'ENOTSUP',
note: 'Bridge send/sendmsg/recv/recvfrom/recvmsg supported for SOCK_STREAM (connected) or SOCK_DGRAM (connected or bound) fds.'
}
}
if (slot.transport === 'tcp') {
if (logicalOp === 'send' || logicalOp === 'sendmsg') {
if (slot.tcpShutWr) {
return {
ok: false,
op: logicalOp,
code: 'EPIPE',
errnoHint: 'EPIPE',
posixAlignment: 'partial',
bridge: true,
transport: 'tcp',
note: 'send after SHUT_WR / half-close write side on TCP bridge (POSIX EPIPE-shaped).'
}
}
let payload
/** @type {{ from: number; to: number }[]} */
let scmRightsLocalDup = []
if (logicalOp === 'sendmsg') {
const prep = prepareBridgeSendmsg(
/** @type {Record<string, unknown>} */ (this),
args && typeof args === 'object'
? /** @type {Record<string, unknown>} */ (args)
: {}
)
if (!prep.ok) return prep.err
payload = prep.payload
scmRightsLocalDup = prep.scmRightsLocalDup
} else {
payload = args.buf ?? args.buffer ?? args.data
if (payload == null) {
return {
ok: false,
op: logicalOp,
code: 'EINVAL',
errnoHint: 'EINVAL'
}
}
if (typeof payload === 'string')
payload = b4a.from(String(payload), 'utf8')
if (!(payload instanceof Uint8Array)) {
return {
ok: false,
op: logicalOp,
code: 'EINVAL',
errnoHint: 'EINVAL',
note: 'send: buf must be Uint8Array or string'
}
}
}
await new Promise((resolve, reject) => {
try {
const w = slot.sock.write(payload, (err) =>
err ? reject(err) : resolve(undefined)
)
if (w === false) {
slot.sock.once('drain', () => resolve(undefined))
}
} catch (e) {
reject(e)
}
})
return {
ok: true,
op: logicalOp,
fd,
bytesSent: payload.byteLength,
bridge: true,
posixAlignment: 'partial',
transport: 'tcp',
...(scmRightsLocalDup.length ? { scmRightsLocalDup } : {})
}
}
const maxBuf = Math.max(
1,
Math.min(
Number(args.len ?? args.length ?? args.max ?? 65536) || 65536,
1024 * 1024
)
)
const nonblock = (this.bareOsLogicalFdFlags[k] & 0x800) === 0x800
const t0 = Date.now()
const blockMax = bareOsPosixAcceptBlockMsMax(shellEnv)
while (true) {
const q = slot.tcpRecvQueue
if (Array.isArray(q) && q.length > 0) {
const row = q.shift()
if (row && row.buf instanceof Uint8Array) {
let out = row.buf
if (out.byteLength > maxBuf) {
const rest = out.slice(maxBuf)
out = out.slice(0, maxBuf)
q.unshift({ buf: rest })
}
const base = {
ok: true,
op: logicalOp,
fd,
bytesReceived: out.byteLength,
buf: out,
bridge: true,
posixAlignment: 'partial',
transport: 'tcp',
tcpRecvDropped: slot.tcpRecvDropped || 0
}
if (logicalOp === 'recvfrom' || logicalOp === 'recvmsg') {
base.address = slot.sock.remoteAddress
base.port = slot.sock.remotePort
}
if (logicalOp === 'recvmsg') {
base.msgHdr = {
schema: 1,
name:
slot.sock.remoteAddress != null
? {
address: String(slot.sock.remoteAddress || ''),
port: Number(slot.sock.remotePort) >>> 0
}
: null,
flags: 0,
controllen: 0,
note: 'TCP bridge: single flat buffer; recv ancillary controllen 0 (socketMsgSurface schema 6).'
}
}
return base
}
}
if (slot.tcpSockError) {
return {
ok: false,
op: logicalOp,
code: 'ECONNRESET',
errnoHint: 'ECONNRESET',
posixAlignment: 'partial',
bridge: true,
transport: 'tcp',
note: 'recv: TCP socket error (bridge)',
tcpRecvDropped: slot.tcpRecvDropped || 0
}
}
if (
slot.tcpPeerEnded &&
(!Array.isArray(slot.tcpRecvQueue) ||
slot.tcpRecvQueue.length === 0)
) {
return {
ok: true,
op: logicalOp,
fd,
bytesReceived: 0,
buf: new Uint8Array(0),
eof: true,
bridge: true,
posixAlignment: 'partial',
transport: 'tcp',
errnoHint: 'EOF',
note: 'recv: EOF after peer half-close / FIN (queue drained); aligns with poll POLLIN readiness.',
tcpRecvDropped: slot.tcpRecvDropped || 0
}
}
if (slot.tcpShutRd && (!q || q.length === 0)) {
return {
ok: true,
op: logicalOp,
fd,
bytesReceived: 0,
buf: new Uint8Array(0),
eof: true,
bridge: true,
posixAlignment: 'partial',
transport: 'tcp',
note: 'recv: local SHUT_RD with empty queue (EOF-shaped).',
tcpRecvDropped: slot.tcpRecvDropped || 0
}
}
if (nonblock) {
return {
ok: false,
op: logicalOp,
code: 'EAGAIN',
errnoHint: 'EAGAIN',
posixAlignment: 'EAGAIN',
bridge: true,
note: 'recv: no data ready (O_NONBLOCK)',
tcpRecvDropped: slot.tcpRecvDropped || 0
}
}
if (Date.now() - t0 >= blockMax) {
return {
ok: false,
op: logicalOp,
code: 'EAGAIN',
errnoHint: 'EAGAIN',
posixAlignment: 'partial',
bridge: true,
note: 'recv: timed out waiting for TCP segment (BARE_OS_POSIX_DGRAM_RECV_BLOCK_MS_MAX budget)',
tcpRecvDropped: slot.tcpRecvDropped || 0
}
}
await bridgeWait(slot, blockMax - (Date.now() - t0), 'recv')
}
}
if (logicalOp === 'send' || logicalOp === 'sendmsg') {
let payload
/** @type {{ from: number; to: number }[]} */
let scmRightsLocalDupUdp = []
if (logicalOp === 'sendmsg') {
const prep = prepareBridgeSendmsg(
/** @type {Record<string, unknown>} */ (this),
args && typeof args === 'object'
? /** @type {Record<string, unknown>} */ (args)
: {}
)
if (!prep.ok) return prep.err
payload = prep.payload
scmRightsLocalDupUdp = prep.scmRightsLocalDup
} else {
payload = args.buf ?? args.buffer ?? args.data
if (payload == null) {
return {
ok: false,
op: logicalOp,
code: 'EINVAL',
errnoHint: 'EINVAL'
}
}
if (typeof payload === 'string')
payload = b4a.from(String(payload), 'utf8')
if (!(payload instanceof Uint8Array)) {
return {
ok: false,
op: logicalOp,
code: 'EINVAL',
errnoHint: 'EINVAL',
note: 'send: buf must be Uint8Array or string'
}
}
}
if (slot.state === 'udp_bound') {
const dp = Number(
args.port ?? args.peerPort ?? args.sin_port ?? args.sinPort
)
const dh = String(
args.host ||
args.address ||
args.peerHost ||
args.sin_addr ||
args.sinAddr ||
''
).trim()
if (!Number.isFinite(dp) || dp <= 0 || dp > 65535 || !dh) {
return {
ok: false,
op: logicalOp,
code: 'EINVAL',
errnoHint: 'EINVAL',
posixAlignment: 'partial',
note: 'send: bound SOCK_DGRAM bridge requires port and host (or connect(2) first for default peer).'
}
}
await new Promise((resolve, reject) => {
try {
slot.sock.send(payload, dp, dh, (err) =>
err ? reject(err) : resolve(undefined)
)
} catch (e) {
reject(e)
}
})
} else {
await new Promise((resolve, reject) => {
try {
slot.sock.send(payload, (err) =>
err ? reject(err) : resolve(undefined)
)
} catch (e) {
reject(e)
}
})
}
return {
ok: true,
op: logicalOp,
fd,
bytesSent: payload.byteLength,
bridge: true,
posixAlignment: 'partial',
...(scmRightsLocalDupUdp.length
? { scmRightsLocalDup: scmRightsLocalDupUdp }
: {})
}
}
const maxBuf = Math.max(
1,
Math.min(
Number(args.len ?? args.length ?? args.max ?? 65536) || 65536,
1024 * 1024
)
)
const nonblock = (this.bareOsLogicalFdFlags[k] & 0x800) === 0x800
const wantFrom = logicalOp === 'recvfrom' || logicalOp === 'recvmsg'
const t0 = Date.now()
const blockMax = bareOsPosixDgramRecvBlockMsMax(shellEnv)
while (true) {
const q = slot.dgramRecvQueue
if (Array.isArray(q) && q.length > 0) {
const row = q.shift()
if (row && row.buf instanceof Uint8Array) {
let out = row.buf
if (out.byteLength > maxBuf) {
const rest = out.slice(maxBuf)
out = out.slice(0, maxBuf)
q.unshift({ buf: rest, rinfo: row.rinfo })
}
const base = {
ok: true,
op: logicalOp,
fd,
bytesReceived: out.byteLength,
buf: out,
bridge: true,
posixAlignment: 'partial',
dgramQueueDepth: q.length,
dgramRecvDropped: slot.dgramRecvDropped || 0
}
if (wantFrom && row.rinfo) {
base.address = row.rinfo.address
base.port = row.rinfo.port
}
if (logicalOp === 'recvmsg') {
base.msgHdr = {
schema: 1,
name: row.rinfo
? {
address: String(row.rinfo.address || ''),
port: Number(row.rinfo.port) >>> 0
}
: null,
flags: 0,
controllen: 0,
note: 'UDP bridge: single flat buf; recv controllen 0; logical SCM_RIGHTS apply only to sendmsg result (schema 4).'
}
}
return base
}
}
if (nonblock) {
return {
ok: false,
op: logicalOp,
code: 'EAGAIN',
errnoHint: 'EAGAIN',
posixAlignment: 'EAGAIN',
bridge: true,
note: 'recv: no datagram ready (O_NONBLOCK)',
dgramRecvDropped: slot.dgramRecvDropped || 0
}
}
if (Date.now() - t0 >= blockMax) {
return {
ok: false,
op: logicalOp,
code: 'EAGAIN',
errnoHint: 'EAGAIN',
posixAlignment: 'partial',
bridge: true,
note: 'recv: timed out waiting for datagram (BARE_OS_POSIX_DGRAM_RECV_BLOCK_MS_MAX)',
dgramRecvDropped: slot.dgramRecvDropped || 0
}
}
await bridgeWait(slot, blockMax - (Date.now() - t0), 'recv')
}
}
}
return {
ok: false,
op: name,
code: 'ENOSYS',
errnoHint: 'ENOSYS',
posixAlignment: 'ENOTSUP',
note: 'POSIX socket-style syscalls are not kernel FDs in Bare OS; use ctx.bare TCP/UDP modules, Hyperswarm, or Protomux-framed streams. Set BARE_OS_POSIX_SOCKET_FD_BRIDGE=1 for partial socket+connect when bareTcp is merged.'
}
}
if (name === 'posixPoll') {
const timeoutMs = Math.max(
0,
Math.min(60000, Math.floor(Number(args.timeoutMs) || 0))
)
if (timeoutMs > 0) {
return this.bareOsPosixPoll({
fds: Array.isArray(args.fds) ? args.fds : [],
timeoutMs
})
}
return this.bareOsPosixPollProbe({
fds: Array.isArray(args.fds) ? args.fds : []
})
}
if (name === 'kill') {
return this.bareOsSendSignal(args.target, String(args.signal || 'TERM'))
}
throw new Error(`bareOsSyscall: unsupported op "${name}"`)
}
}