updates
This commit is contained in:
@@ -0,0 +1,143 @@
|
||||
function parseSize(v) {
|
||||
const s = String(v || '').trim().toLowerCase()
|
||||
const m = /^(\d+)([kmg]?)$/.exec(s)
|
||||
if (!m) return null
|
||||
const n = Number.parseInt(m[1], 10)
|
||||
if (!Number.isFinite(n) || n < 0) return null
|
||||
const mul = m[2] === 'k' ? 1024 : m[2] === 'm' ? 1024 * 1024 : m[2] === 'g' ? 1024 * 1024 * 1024 : 1
|
||||
return n * mul
|
||||
}
|
||||
|
||||
function concatU8(b4a, chunks) {
|
||||
let total = 0
|
||||
for (const c of chunks) total += c.byteLength
|
||||
const out = b4a.alloc(total)
|
||||
let off = 0
|
||||
for (const c of chunks) {
|
||||
out.set(c, off)
|
||||
off += c.byteLength
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
async function run(ctx, argv) {
|
||||
/** @type {{ if?: string, of?: string, bs: number, count?: number, skip: number, seek: number, status: string, conv?: string }} */
|
||||
const opt = { bs: 512, skip: 0, seek: 0, status: 'progress' }
|
||||
|
||||
for (let i = 1; i < argv.length; i++) {
|
||||
const a = argv[i]
|
||||
if (a === '--') continue
|
||||
const eq = a.indexOf('=')
|
||||
if (eq <= 0) {
|
||||
ctx.console.error('dd: expected OPERAND=VALUE: ' + a)
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
const k = a.slice(0, eq)
|
||||
const v = a.slice(eq + 1)
|
||||
if (k === 'if') opt.if = v
|
||||
else if (k === 'of') opt.of = v
|
||||
else if (k === 'bs') {
|
||||
const n = parseSize(v)
|
||||
if (n == null || n <= 0) {
|
||||
ctx.console.error('dd: invalid bs=' + v)
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
opt.bs = n
|
||||
} else if (k === 'count') {
|
||||
const n = Number.parseInt(v, 10)
|
||||
if (!Number.isFinite(n) || n < 0) {
|
||||
ctx.console.error('dd: invalid count=' + v)
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
opt.count = n
|
||||
} else if (k === 'skip') {
|
||||
const n = Number.parseInt(v, 10)
|
||||
if (!Number.isFinite(n) || n < 0) {
|
||||
ctx.console.error('dd: invalid skip=' + v)
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
opt.skip = n
|
||||
} else if (k === 'seek') {
|
||||
const n = Number.parseInt(v, 10)
|
||||
if (!Number.isFinite(n) || n < 0) {
|
||||
ctx.console.error('dd: invalid seek=' + v)
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
opt.seek = n
|
||||
} else if (k === 'status') {
|
||||
if (v !== 'none' && v !== 'progress') {
|
||||
ctx.console.error('dd: unsupported status=' + v)
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
opt.status = v
|
||||
} else if (k === 'conv') {
|
||||
if (v !== 'notrunc') {
|
||||
ctx.console.error('dd: unsupported conv=' + v)
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
opt.conv = v
|
||||
} else {
|
||||
ctx.console.error('dd: unsupported operand ' + k)
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
let src
|
||||
if (opt.if) {
|
||||
src = await ctx.vfs.readFile(opt.if)
|
||||
if (!src) {
|
||||
ctx.console.error('dd: ' + opt.if + ': No such file or directory')
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
} else {
|
||||
src = ctx.b4a.from(bareStdin(ctx), 'utf8')
|
||||
}
|
||||
const input = src instanceof Uint8Array ? src : ctx.b4a.from(src)
|
||||
const start = Math.min(opt.skip * opt.bs, input.byteLength)
|
||||
const end =
|
||||
opt.count == null
|
||||
? input.byteLength
|
||||
: Math.min(input.byteLength, start + opt.count * opt.bs)
|
||||
const chunk = input.subarray(start, end)
|
||||
|
||||
if (!opt.of) {
|
||||
const w = globalThis.process?.stdout?.write
|
||||
if (typeof w === 'function') w.call(globalThis.process.stdout, chunk)
|
||||
else ctx.console.log(ctx.b4a.toString(chunk))
|
||||
if (opt.status !== 'none') {
|
||||
ctx.console.error(`${chunk.byteLength} bytes copied`)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
const outOff = opt.seek * opt.bs
|
||||
let prev = null
|
||||
try {
|
||||
prev = await ctx.vfs.readFile(opt.of)
|
||||
} catch {
|
||||
prev = null
|
||||
}
|
||||
const prevU8 = prev ? (prev instanceof Uint8Array ? prev : ctx.b4a.from(prev)) : ctx.b4a.alloc(0)
|
||||
let out
|
||||
if (opt.conv === 'notrunc') {
|
||||
const want = Math.max(prevU8.byteLength, outOff + chunk.byteLength)
|
||||
out = ctx.b4a.alloc(want)
|
||||
if (prevU8.byteLength) out.set(prevU8, 0)
|
||||
out.set(chunk, outOff)
|
||||
} else {
|
||||
out = concatU8(ctx.b4a, [ctx.b4a.alloc(outOff), chunk])
|
||||
}
|
||||
await ctx.vfs.writeFile(opt.of, out)
|
||||
if (opt.status !== 'none') {
|
||||
ctx.console.error(`${chunk.byteLength} bytes copied`)
|
||||
}
|
||||
}
|
||||
@@ -35,7 +35,14 @@ const CONF = {
|
||||
/** Max input lines `shuf` will hold in memory (override with BARE_OS_SHUF_MAX_LINES). */
|
||||
BARE_OS_SHUF_MAX_LINES: '50000',
|
||||
/** Max output chunk files `split` may create (override with BARE_OS_SPLIT_MAX_FILES). */
|
||||
BARE_OS_SPLIT_MAX_FILES: '10000'
|
||||
BARE_OS_SPLIT_MAX_FILES: '10000',
|
||||
/** Comma-separated `ctx.bareOsSyscall` op names implemented in stock booter. */
|
||||
BARE_OS_SYSCALL_OPS:
|
||||
'readFile,writeFile,readdir,mkdir,stat,unlink,chmod,chdir,getcwd,readlink,symlink,exists,lstat,rmdir,mount,umount,kill',
|
||||
/** Encodings accepted by `/bin/iconv` (subset). */
|
||||
BARE_OS_ICONV_ENCODINGS: 'UTF-8',
|
||||
/** Synthetic process table JSON path (logical VFS). */
|
||||
BARE_OS_PROC_PROCESS_TABLE: '/proc/bare_os/process_table.json'
|
||||
}
|
||||
|
||||
async function run(ctx, argv) {
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* Minimal iconv(1): UTF-8 pass-through only (Bare userland text is UTF-8).
|
||||
*/
|
||||
|
||||
function normEnc(s) {
|
||||
const t = String(s || '')
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.replace(/[-_]/g, '')
|
||||
if (t === 'utf8' || t === 'utf8bom') return 'utf-8'
|
||||
return t
|
||||
}
|
||||
|
||||
async function run(ctx, argv) {
|
||||
let fromEnc = 'utf-8'
|
||||
let toEnc = 'utf-8'
|
||||
let file = null
|
||||
for (let i = 1; i < argv.length; i++) {
|
||||
const a = argv[i]
|
||||
if (a === '--') continue
|
||||
if (a === '-f' || a === '--from-code') {
|
||||
i++
|
||||
if (i >= argv.length) {
|
||||
ctx.console.error('iconv: option requires an argument')
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
fromEnc = normEnc(argv[i])
|
||||
continue
|
||||
}
|
||||
if (a === '-t' || a === '--to-code') {
|
||||
i++
|
||||
if (i >= argv.length) {
|
||||
ctx.console.error('iconv: option requires an argument')
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
toEnc = normEnc(argv[i])
|
||||
continue
|
||||
}
|
||||
if (a.startsWith('-')) {
|
||||
ctx.console.error('iconv: unsupported option: ' + a)
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
file = a
|
||||
break
|
||||
}
|
||||
|
||||
if (fromEnc !== 'utf-8' || toEnc !== 'utf-8') {
|
||||
ctx.console.error(
|
||||
'iconv: only UTF-8 to UTF-8 is supported in this build (see getconf BARE_OS_ICONV_ENCODINGS)'
|
||||
)
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
|
||||
let u8
|
||||
if (file) {
|
||||
const raw = await ctx.vfs.readFile(file)
|
||||
if (raw == null) {
|
||||
ctx.console.error('iconv: ' + file + ': cannot read')
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
u8 = raw instanceof Uint8Array ? raw : ctx.b4a.from(raw)
|
||||
} else {
|
||||
u8 = ctx.b4a.from(bareStdin(ctx), 'utf8')
|
||||
}
|
||||
|
||||
const w = globalThis.process?.stdout?.write
|
||||
if (typeof w === 'function') w.call(globalThis.process.stdout, u8)
|
||||
else ctx.console.log(ctx.b4a.toString(u8, 'utf8'))
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
async function run(ctx, argv) {
|
||||
const args = argv.slice(1)
|
||||
let signal = 'TERM'
|
||||
/** @type {string[]} */
|
||||
const targets = []
|
||||
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
const a = args[i]
|
||||
if (a === '--') {
|
||||
for (let j = i + 1; j < args.length; j++) targets.push(args[j])
|
||||
break
|
||||
}
|
||||
if (a === '-l' || a === '--list') {
|
||||
ctx.console.log('HUP INT KILL TERM 0')
|
||||
return
|
||||
}
|
||||
if (a === '-s' || a === '--signal') {
|
||||
const n = args[i + 1]
|
||||
if (!n) {
|
||||
ctx.console.error('kill: option requires an argument: ' + a)
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
signal = n
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (/^-[A-Za-z0-9]+$/.test(a) && a !== '-') {
|
||||
signal = a.slice(1)
|
||||
continue
|
||||
}
|
||||
targets.push(a)
|
||||
}
|
||||
|
||||
if (targets.length === 0) {
|
||||
ctx.console.error('usage: kill [-s SIGNAL | -SIGNAL] <pid|name>...')
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
const send = ctx.bareOsSendSignal
|
||||
if (typeof send !== 'function') {
|
||||
ctx.console.error('kill: signal API unavailable in this runtime')
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
|
||||
let failed = 0
|
||||
for (const t of targets) {
|
||||
try {
|
||||
send.call(ctx, t, signal)
|
||||
} catch (e) {
|
||||
failed++
|
||||
ctx.console.error('kill: ' + t + ': ' + (e?.message || String(e)))
|
||||
}
|
||||
}
|
||||
if (failed > 0) ctx.exitCode = 1
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
function parseLoggerArgv(argv) {
|
||||
let tag = 'logger'
|
||||
let prio = 'user.notice'
|
||||
let i = 1
|
||||
const msg = []
|
||||
while (i < argv.length) {
|
||||
const a = argv[i]
|
||||
if (a === '--') {
|
||||
msg.push(...argv.slice(i + 1))
|
||||
break
|
||||
}
|
||||
if (a === '-t') {
|
||||
i++
|
||||
if (i >= argv.length) return { err: 'logger: option requires an argument -- t' }
|
||||
tag = String(argv[i] || '').trim().slice(0, 64) || 'logger'
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (a === '-p') {
|
||||
i++
|
||||
if (i >= argv.length) return { err: 'logger: option requires an argument -- p' }
|
||||
prio = String(argv[i] || '').trim().slice(0, 64) || 'user.notice'
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (a.startsWith('-')) return { err: 'logger: unsupported option ' + a }
|
||||
msg.push(...argv.slice(i))
|
||||
break
|
||||
}
|
||||
return { tag, prio, text: msg.join(' ') }
|
||||
}
|
||||
|
||||
async function run(ctx, argv) {
|
||||
const p = parseLoggerArgv(argv)
|
||||
if (p.err) {
|
||||
ctx.console.error(p.err)
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
let text = String(p.text || '')
|
||||
if (!text) text = bareStdin(ctx).trim()
|
||||
if (!text) {
|
||||
ctx.console.error('logger: empty message')
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
|
||||
const rec = {
|
||||
ts: new Date().toISOString(),
|
||||
tag: p.tag,
|
||||
priority: p.prio,
|
||||
message: text
|
||||
}
|
||||
const line = JSON.stringify(rec) + '\n'
|
||||
const maxRaw = Number.parseInt(String(ctx.vfs?.env?.BARE_OS_LOGGER_MAX_BYTES || '1048576'), 10)
|
||||
const maxBytes = Number.isFinite(maxRaw) && maxRaw > 1024 ? Math.min(maxRaw, 8 * 1024 * 1024) : 1048576
|
||||
let prev = ctx.b4a.from('')
|
||||
try {
|
||||
const cur = await ctx.vfs.readFile('/var/log/messages')
|
||||
if (cur) prev = cur instanceof Uint8Array ? cur : ctx.b4a.from(cur)
|
||||
} catch {
|
||||
/* new file */
|
||||
}
|
||||
let next = ctx.b4a.concat([prev, ctx.b4a.from(line, 'utf8')])
|
||||
if (next.byteLength > maxBytes) {
|
||||
next = next.subarray(next.byteLength - maxBytes)
|
||||
}
|
||||
await ctx.vfs.writeFile('/var/log/messages', next)
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
async function printMountTable(ctx) {
|
||||
try {
|
||||
const buf = await ctx.vfs.readFile('/proc/mounts')
|
||||
if (buf) {
|
||||
const s = ctx.b4a.toString(buf)
|
||||
if (s) {
|
||||
ctx.console.log(s.endsWith('\n') ? s.slice(0, -1) : s)
|
||||
return
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
/* fall through */
|
||||
}
|
||||
ctx.console.log('(no mounts)')
|
||||
}
|
||||
|
||||
async function run(ctx, argv) {
|
||||
const args = argv.slice(1)
|
||||
if (!args.length) {
|
||||
await printMountTable(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
let type = 'hyperdrive'
|
||||
let opts = ''
|
||||
/** @type {string[]} */
|
||||
const pos = []
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
const a = args[i]
|
||||
if (a === '-t') {
|
||||
i++
|
||||
if (i >= args.length) {
|
||||
ctx.console.error('mount: option requires an argument -- t')
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
type = String(args[i] || '')
|
||||
continue
|
||||
}
|
||||
if (a === '-o') {
|
||||
i++
|
||||
if (i >= args.length) {
|
||||
ctx.console.error('mount: option requires an argument -- o')
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
opts = String(args[i] || '')
|
||||
continue
|
||||
}
|
||||
if (a.startsWith('-')) {
|
||||
ctx.console.error('mount: unsupported option ' + a)
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
pos.push(a)
|
||||
}
|
||||
|
||||
if (type !== 'hyperdrive') {
|
||||
ctx.console.error('mount: only -t hyperdrive is supported')
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
if (pos.length < 2) {
|
||||
ctx.console.error(
|
||||
'usage: mount [-t hyperdrive] [-o ro] <source-key|local> </mnt/label>'
|
||||
)
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
const source = String(pos[0] || '').trim()
|
||||
const target = String(pos[1] || '').trim()
|
||||
const m = /^\/mnt\/([a-zA-Z0-9][a-zA-Z0-9._-]{0,62})$/.exec(target)
|
||||
if (!m) {
|
||||
ctx.console.error('mount: target must be /mnt/<label>')
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
const label = m[1]
|
||||
const ro = opts
|
||||
.split(',')
|
||||
.map((x) => x.trim())
|
||||
.filter(Boolean)
|
||||
.includes('ro')
|
||||
|
||||
if (typeof ctx.bareOsSyscall === 'function') {
|
||||
try {
|
||||
const out = await ctx.bareOsSyscall('mount', {
|
||||
source,
|
||||
target,
|
||||
label,
|
||||
readOnly: ro
|
||||
})
|
||||
if (out && typeof out === 'object' && out.note) {
|
||||
ctx.console.log(String(out.note))
|
||||
}
|
||||
return
|
||||
} catch (e) {
|
||||
ctx.console.error('mount: ' + (e?.message || String(e)))
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof ctx.runHdms !== 'function') {
|
||||
ctx.console.error('mount: hdms bridge unavailable')
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
try {
|
||||
if (source === 'local') await ctx.runHdms(['hdms', 'create', label])
|
||||
else await ctx.runHdms(['hdms', 'add', label, source])
|
||||
} catch (e) {
|
||||
ctx.console.error('mount: ' + (e?.message || String(e)))
|
||||
ctx.exitCode = 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
async function run(ctx, argv) {
|
||||
void argv
|
||||
let buf = null
|
||||
try {
|
||||
buf = await ctx.vfs.readFile('/proc/bare_os/process_table.json')
|
||||
} catch {
|
||||
buf = null
|
||||
}
|
||||
if (!buf) {
|
||||
ctx.console.error('procstat: process table unavailable')
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
let table
|
||||
try {
|
||||
table = JSON.parse(ctx.b4a.toString(buf))
|
||||
} catch {
|
||||
ctx.console.error('procstat: invalid process table payload')
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
const rows = Array.isArray(table.processes) ? table.processes : []
|
||||
ctx.console.log(
|
||||
JSON.stringify(
|
||||
{
|
||||
schema: 1,
|
||||
atMs: Date.now(),
|
||||
processCount: rows.length,
|
||||
processes: rows
|
||||
},
|
||||
null,
|
||||
2
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
function pad(v, n) {
|
||||
const s = String(v == null ? '' : v)
|
||||
return s.length >= n ? s : s + ' '.repeat(n - s.length)
|
||||
}
|
||||
|
||||
async function run(ctx, argv) {
|
||||
void argv
|
||||
const read = ctx.bareOsReadProcessTable
|
||||
if (typeof read !== 'function') {
|
||||
ctx.console.error('ps: process table API unavailable in this runtime')
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
let snap
|
||||
try {
|
||||
snap = read.call(ctx) || {}
|
||||
} catch (e) {
|
||||
ctx.console.error('ps: unable to read process table: ' + (e?.message || e))
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
const rows = Array.isArray(snap.processes) ? snap.processes : []
|
||||
ctx.console.log(
|
||||
`${pad('PID', 6)}${pad('PPID', 6)}${pad('STATE', 10)}${pad('NAME', 18)}SID`
|
||||
)
|
||||
for (const r of rows) {
|
||||
ctx.console.log(
|
||||
`${pad(r.pid ?? '', 6)}${pad(r.ppid ?? '', 6)}${pad(
|
||||
r.state ?? '',
|
||||
10
|
||||
)}${pad(r.name ?? '', 18)}${r.sessionId || ''}`
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Subset of ulimit(1): reports soft limits aligned with getconf(1) caps (no host rlimit).
|
||||
*/
|
||||
|
||||
const LIMITS = [
|
||||
['open files', '-n', '256'],
|
||||
['max user processes', '-u', '32'],
|
||||
['pipe size', '-p', '512'],
|
||||
['file size', '-f', 'unlimited'],
|
||||
['cpu time', '-t', 'unlimited'],
|
||||
['virtual memory', '-v', 'unlimited']
|
||||
]
|
||||
|
||||
async function run(ctx, argv) {
|
||||
const args = argv.slice(1).filter((a) => a !== '--')
|
||||
if (args.length === 0 || (args.length === 1 && args[0] === '-a')) {
|
||||
for (const [label, flag, val] of LIMITS) {
|
||||
ctx.console.log(`${label} (${flag}) ${val}`)
|
||||
}
|
||||
return
|
||||
}
|
||||
if (args.length === 1 && args[0] === '-n') {
|
||||
ctx.console.log('256')
|
||||
return
|
||||
}
|
||||
ctx.console.error('ulimit: only -a and -n are supported in Bare OS')
|
||||
ctx.exitCode = 1
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
async function run(ctx, argv) {
|
||||
const args = argv.slice(1).filter((a) => a !== '--')
|
||||
if (!args.length) {
|
||||
ctx.console.error('usage: umount </mnt/label>')
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
const target = String(args[0] || '').trim()
|
||||
const m = /^\/mnt\/([a-zA-Z0-9][a-zA-Z0-9._-]{0,62})$/.exec(target)
|
||||
if (!m) {
|
||||
ctx.console.error('umount: target must be /mnt/<label>')
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
const label = m[1]
|
||||
|
||||
if (typeof ctx.bareOsSyscall === 'function') {
|
||||
try {
|
||||
await ctx.bareOsSyscall('umount', { target, label })
|
||||
return
|
||||
} catch (e) {
|
||||
ctx.console.error('umount: ' + (e?.message || String(e)))
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof ctx.runHdms !== 'function') {
|
||||
ctx.console.error('umount: hdms bridge unavailable')
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
try {
|
||||
await ctx.runHdms(['hdms', 'remove', label])
|
||||
} catch (e) {
|
||||
ctx.console.error('umount: ' + (e?.message || String(e)))
|
||||
ctx.exitCode = 1
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user