feat(userland): NipBox LITE subject binary, ARM64 init support
This commit is contained in:
parent
47f1078748
commit
dfe9135b8a
438
nipbox.nim
438
nipbox.nim
|
|
@ -12,9 +12,49 @@ import strutils, parseutils, tables, sequtils, json
|
||||||
import kdl
|
import kdl
|
||||||
import ../../libs/membrane/libc as lb
|
import ../../libs/membrane/libc as lb
|
||||||
import ../../libs/membrane/libc_net as lnet
|
import ../../libs/membrane/libc_net as lnet
|
||||||
import ../../libs/membrane/fs/sfs_user as sfs
|
when not defined(NIPBOX_LITE):
|
||||||
|
import ../../libs/membrane/fs/sfs_user as sfs
|
||||||
import editor
|
import editor
|
||||||
import ../../libs/membrane/term # Phase 26: Visual Cortex
|
when not defined(NIPBOX_LITE):
|
||||||
|
import ../../libs/membrane/term # Phase 26: Visual Cortex
|
||||||
|
|
||||||
|
# --- M4.4: BKDL Capability Manifest (SPEC-071) ---
|
||||||
|
{.emit: """
|
||||||
|
__attribute__((section(".nexus.manifest"), used))
|
||||||
|
static const unsigned char nexus_manifest[166] = {
|
||||||
|
/* BkdlHeader (118 bytes) */
|
||||||
|
0x53, 0x55, 0x58, 0x4E, /* magic: "NXUS" (LE) */
|
||||||
|
0x01, 0x00, /* version: 1 */
|
||||||
|
0x00, 0x00, /* flags: 0 */
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* signature[0..63]: zeros */
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* pubkey_hash[0..31]: zeros */
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x04, 0x00, /* cap_count: 4 */
|
||||||
|
0x00, 0x00, 0x00, 0x00, /* blob_size: 0 */
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* entry_point: 0 */
|
||||||
|
/* CapDescriptor[0]: console.output (0x1001) WRITE */
|
||||||
|
0x02, 0x02, 0x00, 0x00,
|
||||||
|
0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
/* CapDescriptor[1]: VFS (0x2000) READ */
|
||||||
|
0x02, 0x01, 0x00, 0x00,
|
||||||
|
0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
/* CapDescriptor[2]: NET_TX (0x0500) WRITE */
|
||||||
|
0x02, 0x02, 0x00, 0x00,
|
||||||
|
0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
/* CapDescriptor[3]: NET_RX (0x0501) READ */
|
||||||
|
0x02, 0x01, 0x00, 0x00,
|
||||||
|
0x01, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||||
|
};
|
||||||
|
""".}
|
||||||
|
|
||||||
# Phase 30: Pledge Constants
|
# Phase 30: Pledge Constants
|
||||||
const
|
const
|
||||||
|
|
@ -221,7 +261,7 @@ proc cmd_cp*(args: seq[string], input: PipelineData): PipelineData =
|
||||||
return @[]
|
return @[]
|
||||||
|
|
||||||
# O_WRONLY(1) | O_CREAT(64) | O_TRUNC(512) = 577
|
# O_WRONLY(1) | O_CREAT(64) | O_TRUNC(512) = 577
|
||||||
let fd_dest = lb.open(dest.cstring, 577)
|
let fd_dest = lb.open(dest.cstring, 577)
|
||||||
if fd_dest < 0:
|
if fd_dest < 0:
|
||||||
print("cp: cannot create '" & dest & "'\n")
|
print("cp: cannot create '" & dest & "'\n")
|
||||||
discard lb.close(fd_src)
|
discard lb.close(fd_src)
|
||||||
|
|
@ -247,11 +287,11 @@ proc cmd_mv*(args: seq[string], input: PipelineData): PipelineData =
|
||||||
if args.len < 2:
|
if args.len < 2:
|
||||||
print("Usage: mv <source> <dest>\n")
|
print("Usage: mv <source> <dest>\n")
|
||||||
return @[]
|
return @[]
|
||||||
|
|
||||||
# Step 1: Copy
|
# Step 1: Copy
|
||||||
print("[mv] Copying...\n")
|
print("[mv] Copying...\n")
|
||||||
discard cmd_cp(args, input)
|
discard cmd_cp(args, input)
|
||||||
|
|
||||||
# Step 2: Unlink (Not yet supported by Kernel)
|
# Step 2: Unlink (Not yet supported by Kernel)
|
||||||
print("[mv] Warning: Original file '" & args[0] & "' retained (SFS unlink not implemented).\n")
|
print("[mv] Warning: Original file '" & args[0] & "' retained (SFS unlink not implemented).\n")
|
||||||
return @[]
|
return @[]
|
||||||
|
|
@ -260,7 +300,7 @@ proc cmd_touch*(args: seq[string], input: PipelineData): PipelineData =
|
||||||
if args.len < 1:
|
if args.len < 1:
|
||||||
print("Usage: touch <filename>\n")
|
print("Usage: touch <filename>\n")
|
||||||
return @[]
|
return @[]
|
||||||
|
|
||||||
let fd = lb.open(args[0].cstring, 577) # O_CREAT | O_TRUNC
|
let fd = lb.open(args[0].cstring, 577) # O_CREAT | O_TRUNC
|
||||||
if fd >= 0:
|
if fd >= 0:
|
||||||
discard lb.close(fd)
|
discard lb.close(fd)
|
||||||
|
|
@ -291,45 +331,53 @@ proc cmd_cat*(args: seq[string], input: PipelineData): PipelineData =
|
||||||
proc cmd_write*(args: seq[string], input: PipelineData): PipelineData =
|
proc cmd_write*(args: seq[string], input: PipelineData): PipelineData =
|
||||||
## write <filename> <content>
|
## write <filename> <content>
|
||||||
## Uses USERLAND SFS (Block Valve architecture)
|
## Uses USERLAND SFS (Block Valve architecture)
|
||||||
if args.len < 2:
|
when not defined(NIPBOX_LITE):
|
||||||
print("Usage: write <filename> <content>\n")
|
if args.len < 2:
|
||||||
|
print("Usage: write <filename> <content>\n")
|
||||||
|
return @[]
|
||||||
|
|
||||||
|
let filename = args[0]
|
||||||
|
let content = args[1..^1].join(" ")
|
||||||
|
|
||||||
|
# Mount userland FS if not already done
|
||||||
|
if not sfs.sfs_is_mounted():
|
||||||
|
discard sfs.sfs_mount()
|
||||||
|
|
||||||
|
let bytes_written = sfs.sfs_write(filename, cast[pointer](unsafeAddr content[0]), content.len)
|
||||||
|
if bytes_written > 0:
|
||||||
|
print("[Glass Vault] Written " & $bytes_written & " bytes to: " & filename & " (Userland SFS)\n")
|
||||||
|
else:
|
||||||
|
print("Error: Could not write to " & filename & "\n")
|
||||||
return @[]
|
return @[]
|
||||||
|
|
||||||
let filename = args[0]
|
|
||||||
let content = args[1..^1].join(" ")
|
|
||||||
|
|
||||||
# Mount userland FS if not already done
|
|
||||||
if not sfs.sfs_is_mounted():
|
|
||||||
discard sfs.sfs_mount()
|
|
||||||
|
|
||||||
let bytes_written = sfs.sfs_write(filename, cast[pointer](unsafeAddr content[0]), content.len)
|
|
||||||
if bytes_written > 0:
|
|
||||||
print("[Glass Vault] Written " & $bytes_written & " bytes to: " & filename & " (Userland SFS)\n")
|
|
||||||
else:
|
else:
|
||||||
print("Error: Could not write to " & filename & "\n")
|
print("[nipbox] 'write' requires SFS (not available in lite mode)\n")
|
||||||
return @[]
|
return @[]
|
||||||
|
|
||||||
proc cmd_read*(args: seq[string], input: PipelineData): PipelineData =
|
proc cmd_read*(args: seq[string], input: PipelineData): PipelineData =
|
||||||
## read <filename>
|
## read <filename>
|
||||||
## Uses USERLAND SFS (Block Valve architecture)
|
## Uses USERLAND SFS (Block Valve architecture)
|
||||||
if args.len == 0:
|
when not defined(NIPBOX_LITE):
|
||||||
print("Usage: read <filename>\n")
|
if args.len == 0:
|
||||||
|
print("Usage: read <filename>\n")
|
||||||
|
return @[]
|
||||||
|
|
||||||
|
let filename = args[0]
|
||||||
|
|
||||||
|
# Mount userland FS if not already done
|
||||||
|
if not sfs.sfs_is_mounted():
|
||||||
|
discard sfs.sfs_mount()
|
||||||
|
|
||||||
|
var buf: array[4096, char]
|
||||||
|
let bytes_read = sfs.sfs_read(filename, addr buf[0], 4096)
|
||||||
|
if bytes_read > 0:
|
||||||
|
discard lb.write(cint(1), addr buf[0], csize_t(bytes_read))
|
||||||
|
print("\n[Glass Vault] Read " & $bytes_read & " bytes from: " & filename & " (Userland SFS)\n")
|
||||||
|
else:
|
||||||
|
print("Error: Could not open " & filename & "\n")
|
||||||
return @[]
|
return @[]
|
||||||
|
|
||||||
let filename = args[0]
|
|
||||||
|
|
||||||
# Mount userland FS if not already done
|
|
||||||
if not sfs.sfs_is_mounted():
|
|
||||||
discard sfs.sfs_mount()
|
|
||||||
|
|
||||||
var buf: array[4096, char]
|
|
||||||
let bytes_read = sfs.sfs_read(filename, addr buf[0], 4096)
|
|
||||||
if bytes_read > 0:
|
|
||||||
discard lb.write(cint(1), addr buf[0], csize_t(bytes_read))
|
|
||||||
print("\n[Glass Vault] Read " & $bytes_read & " bytes from: " & filename & " (Userland SFS)\n")
|
|
||||||
else:
|
else:
|
||||||
print("Error: Could not open " & filename & "\n")
|
print("[nipbox] 'read' requires SFS (not available in lite mode)\n")
|
||||||
return @[]
|
return @[]
|
||||||
|
|
||||||
proc cmd_edit*(args: seq[string], input: PipelineData): PipelineData =
|
proc cmd_edit*(args: seq[string], input: PipelineData): PipelineData =
|
||||||
if args.len == 0:
|
if args.len == 0:
|
||||||
|
|
@ -482,239 +530,223 @@ proc cmd_http_get*(args: seq[string], input: PipelineData): PipelineData =
|
||||||
return @[node]
|
return @[node]
|
||||||
|
|
||||||
proc cmd_http_download*(args: seq[string], input: PipelineData): PipelineData =
|
proc cmd_http_download*(args: seq[string], input: PipelineData): PipelineData =
|
||||||
if args.len < 2:
|
when not defined(NIPBOX_LITE):
|
||||||
print("Usage: http.download <ip:port/path> <outfile>\n")
|
if args.len < 2:
|
||||||
return @[]
|
print("Usage: http.download <ip:port/path> <outfile>\n")
|
||||||
|
return @[]
|
||||||
|
|
||||||
let url_arg = args[0]
|
let url_arg = args[0]
|
||||||
let outfile = args[1]
|
let outfile = args[1]
|
||||||
|
|
||||||
var host_part = url_arg
|
var host_part = url_arg
|
||||||
var path_str = "/"
|
var path_str = "/"
|
||||||
|
|
||||||
let slash_pos = url_arg.find('/')
|
let slash_pos = url_arg.find('/')
|
||||||
if slash_pos != -1:
|
if slash_pos != -1:
|
||||||
host_part = url_arg[0..<slash_pos]
|
host_part = url_arg[0..<slash_pos]
|
||||||
path_str = url_arg[slash_pos..^1]
|
path_str = url_arg[slash_pos..^1]
|
||||||
|
|
||||||
let parts = host_part.split(':')
|
let parts = host_part.split(':')
|
||||||
if parts.len != 2:
|
if parts.len != 2:
|
||||||
print("Error: Target must be IP:PORT (e.g. 10.0.2.2:8000)\n")
|
print("Error: Target must be IP:PORT (e.g. 10.0.2.2:8000)\n")
|
||||||
return @[]
|
return @[]
|
||||||
|
|
||||||
let ip_str = parts[0]
|
let ip_str = parts[0]
|
||||||
let port = uint16(parseInt(parts[1]))
|
let port = uint16(parseInt(parts[1]))
|
||||||
|
|
||||||
# Parse IP
|
# Parse IP
|
||||||
var ip_val: uint32 = 0
|
var ip_val: uint32 = 0
|
||||||
try:
|
try:
|
||||||
let p = ip_str.split('.')
|
let p = ip_str.split('.')
|
||||||
ip_val = (uint32(parseInt(p[0])) and 0xFF) or
|
ip_val = (uint32(parseInt(p[0])) and 0xFF) or
|
||||||
((uint32(parseInt(p[1])) and 0xFF) shl 8) or
|
((uint32(parseInt(p[1])) and 0xFF) shl 8) or
|
||||||
((uint32(parseInt(p[2])) and 0xFF) shl 16) or
|
((uint32(parseInt(p[2])) and 0xFF) shl 16) or
|
||||||
((uint32(parseInt(p[3])) and 0xFF) shl 24)
|
((uint32(parseInt(p[3])) and 0xFF) shl 24)
|
||||||
except:
|
except:
|
||||||
print("Error: Invalid IP\n")
|
print("Error: Invalid IP\n")
|
||||||
return @[]
|
return @[]
|
||||||
|
|
||||||
print("[Download] Connecting to " & host_part & "...\n")
|
print("[Download] Connecting to " & host_part & "...\n")
|
||||||
let fd = lb.socket(2, 1, 0)
|
let fd = lb.socket(2, 1, 0)
|
||||||
if fd < 0: return @[]
|
if fd < 0: return @[]
|
||||||
|
|
||||||
# SockAddr setup
|
# SockAddr setup
|
||||||
var addr_buf: array[16, byte]
|
var addr_buf: array[16, byte]
|
||||||
copyMem(addr addr_buf[2], unsafeAddr port, 2)
|
copyMem(addr addr_buf[2], unsafeAddr port, 2)
|
||||||
copyMem(addr addr_buf[4], unsafeAddr ip_val, 4)
|
copyMem(addr addr_buf[4], unsafeAddr ip_val, 4)
|
||||||
|
|
||||||
if lb.connect(fd, addr addr_buf[0], 16) < 0:
|
if lb.connect(fd, addr addr_buf[0], 16) < 0:
|
||||||
print("Error: Connection Failed.\n")
|
print("Error: Connection Failed.\n")
|
||||||
return @[]
|
return @[]
|
||||||
|
|
||||||
# Request
|
# Request
|
||||||
let req = "GET " & path_str & " HTTP/1.0\r\nHost: " & ip_str & "\r\nConnection: close\r\n\r\n"
|
let req = "GET " & path_str & " HTTP/1.0\r\nHost: " & ip_str & "\r\nConnection: close\r\n\r\n"
|
||||||
if lb.send(cint(fd), cast[pointer](unsafeAddr req[0]), csize_t(req.len), 0) <= 0:
|
if lb.send(cint(fd), cast[pointer](unsafeAddr req[0]), csize_t(req.len), 0) <= 0:
|
||||||
print("Error: Send Failed.\n")
|
print("Error: Send Failed.\n")
|
||||||
discard lb.close(cint(fd))
|
|
||||||
return @[]
|
|
||||||
|
|
||||||
# Mount SFS if needed
|
|
||||||
if not sfs.sfs_is_mounted():
|
|
||||||
if not sfs.sfs_mount():
|
|
||||||
print("Error: Could not mount SFS.\n")
|
|
||||||
discard lb.close(cint(fd))
|
discard lb.close(cint(fd))
|
||||||
return @[]
|
return @[]
|
||||||
|
|
||||||
# Open SFS Stream
|
# Mount SFS if needed
|
||||||
let sfs_h = sfs.sfs_open_write(outfile)
|
if not sfs.sfs_is_mounted():
|
||||||
if sfs_h == nil:
|
if not sfs.sfs_mount():
|
||||||
print("Error: Could not create file " & outfile & "\n")
|
print("Error: Could not mount SFS.\n")
|
||||||
discard lb.close(cint(fd))
|
discard lb.close(cint(fd))
|
||||||
return @[]
|
return @[]
|
||||||
|
|
||||||
print("[Download] Downloading...\n")
|
# Open SFS Stream
|
||||||
|
let sfs_h = sfs.sfs_open_write(outfile)
|
||||||
|
if sfs_h == nil:
|
||||||
|
print("Error: Could not create file " & outfile & "\n")
|
||||||
|
discard lb.close(cint(fd))
|
||||||
|
return @[]
|
||||||
|
|
||||||
var buf: array[4096, char]
|
print("[Download] Downloading...\n")
|
||||||
var header_acc = ""
|
|
||||||
var header_parsed = false
|
|
||||||
var total_bytes = 0
|
|
||||||
var content_len = -1
|
|
||||||
var timeout = 0
|
|
||||||
|
|
||||||
while timeout < 10000:
|
var buf: array[4096, char]
|
||||||
# Use libc shim which pumps stack
|
var header_acc = ""
|
||||||
let n = lb.recv(cint(fd), addr buf[0], 4096, 0)
|
var header_parsed = false
|
||||||
|
var total_bytes = 0
|
||||||
|
var content_len = -1
|
||||||
|
var timeout = 0
|
||||||
|
|
||||||
if n > 0:
|
while timeout < 10000:
|
||||||
timeout = 0
|
let n = lb.recv(cint(fd), addr buf[0], 4096, 0)
|
||||||
if not header_parsed:
|
|
||||||
for i in 0..<n: header_acc.add(buf[i])
|
if n > 0:
|
||||||
let sep = header_acc.find("\r\n\r\n")
|
timeout = 0
|
||||||
if sep != -1:
|
if not header_parsed:
|
||||||
header_parsed = true
|
for i in 0..<n: header_acc.add(buf[i])
|
||||||
|
let sep = header_acc.find("\r\n\r\n")
|
||||||
# Try to find Content-Length
|
if sep != -1:
|
||||||
# Quick hacky parse
|
header_parsed = true
|
||||||
let lower_head = header_acc.toLowerAscii()
|
|
||||||
let cl_idx = lower_head.find("content-length:")
|
let lower_head = header_acc.toLowerAscii()
|
||||||
if cl_idx != -1:
|
let cl_idx = lower_head.find("content-length:")
|
||||||
let end_line = lower_head.find("\r\n", cl_idx)
|
if cl_idx != -1:
|
||||||
if end_line != -1:
|
let end_line = lower_head.find("\r\n", cl_idx)
|
||||||
try:
|
if end_line != -1:
|
||||||
content_len = parseInt(lower_head[cl_idx+15..<end_line].strip())
|
try:
|
||||||
except:
|
content_len = parseInt(lower_head[cl_idx+15..<end_line].strip())
|
||||||
content_len = -1
|
except:
|
||||||
|
content_len = -1
|
||||||
let body_start = sep + 4
|
|
||||||
if body_start < header_acc.len:
|
let body_start = sep + 4
|
||||||
let chunk = header_acc[body_start..^1]
|
if body_start < header_acc.len:
|
||||||
sfs.sfs_write_chunk(sfs_h, cast[pointer](unsafeAddr chunk[0]), chunk.len)
|
let chunk = header_acc[body_start..^1]
|
||||||
total_bytes += chunk.len
|
sfs.sfs_write_chunk(sfs_h, cast[pointer](unsafeAddr chunk[0]), chunk.len)
|
||||||
header_acc = ""
|
total_bytes += chunk.len
|
||||||
|
header_acc = ""
|
||||||
|
else:
|
||||||
|
if header_acc.len > 8192:
|
||||||
|
print("Error: Headers too large.\n")
|
||||||
|
break
|
||||||
else:
|
else:
|
||||||
if header_acc.len > 8192:
|
sfs.sfs_write_chunk(sfs_h, addr buf[0], int(n))
|
||||||
print("Error: Headers too large.\n")
|
total_bytes += int(n)
|
||||||
break
|
|
||||||
|
if content_len > 0:
|
||||||
|
if total_bytes mod 10240 < int(n): print(".")
|
||||||
|
else:
|
||||||
|
if total_bytes mod 10240 < int(n): print(".")
|
||||||
|
|
||||||
|
elif n == 0:
|
||||||
|
break
|
||||||
else:
|
else:
|
||||||
# Stream directly to SFS
|
break
|
||||||
sfs.sfs_write_chunk(sfs_h, addr buf[0], int(n))
|
|
||||||
total_bytes += int(n)
|
|
||||||
|
|
||||||
# Progress Bar
|
|
||||||
if content_len > 0:
|
|
||||||
# let pct = (total_bytes * 100) div content_len
|
|
||||||
if total_bytes mod 10240 < int(n): print(".")
|
|
||||||
else:
|
|
||||||
if total_bytes mod 10240 < int(n): print(".")
|
|
||||||
|
|
||||||
elif n == 0:
|
sfs.sfs_close_write(sfs_h)
|
||||||
break
|
discard lb.close(cint(fd))
|
||||||
else:
|
print("\n[Download] Complete. " & $total_bytes & " bytes written to " & outfile & " (Glass Vault).\n")
|
||||||
timeout += 1
|
return @[]
|
||||||
# Busy wait / pump handled in recv?
|
else:
|
||||||
# Recv calls pump_membrane_stack loop
|
print("[nipbox] 'http.download' requires SFS (not available in lite mode)\n")
|
||||||
# But if we return -1 (EAGAIN), we need to retry.
|
return @[]
|
||||||
# My libc.libc_recv returns 0 on closed?
|
|
||||||
# Actually libc_recv in step 945 waits until data or closed.
|
|
||||||
# So n==0 means closed.
|
|
||||||
# Wait, libc.nim recv implementation:
|
|
||||||
# while true: pump; if data return n; if closed return 0.
|
|
||||||
# So it blocks until data.
|
|
||||||
# Thus n > 0 always unless closed.
|
|
||||||
break
|
|
||||||
|
|
||||||
sfs.sfs_close_write(sfs_h)
|
|
||||||
discard lb.close(cint(fd))
|
|
||||||
print("\n[Download] Complete. " & $total_bytes & " bytes written to " & outfile & " (Glass Vault).\n")
|
|
||||||
return @[]
|
|
||||||
|
|
||||||
# Phase 37: HTTP Verification Tool
|
# Phase 37: HTTP Verification Tool
|
||||||
proc cmd_http_test*(args: seq[string], input: PipelineData): PipelineData =
|
proc cmd_http_test*(args: seq[string], input: PipelineData): PipelineData =
|
||||||
if args.len < 1:
|
if args.len < 1:
|
||||||
print("Usage: http <host>\n")
|
print("Usage: http.test <host>\n")
|
||||||
return @[]
|
return @[]
|
||||||
|
|
||||||
let host = args[0]
|
let host = args[0]
|
||||||
print("Dialing " & host & ":80...\n")
|
print("Dialing " & host & ":80...\n")
|
||||||
|
|
||||||
let fd = lnet.net_dial_tcp(host, 80)
|
let fd = lnet.net_dial_tcp(host, 80)
|
||||||
if fd < 0:
|
if fd < 0:
|
||||||
print("Connection Failed! Error: " & $fd & "\n")
|
print("Connection Failed! Error: " & $fd & "\n")
|
||||||
return @[]
|
return @[]
|
||||||
|
|
||||||
print("Connected! Sending GET request...\n")
|
print("Connected! Sending GET request...\n")
|
||||||
discard lnet.net_send(fd, "GET / HTTP/1.0\r\nHost: " & host & "\r\nConnection: close\r\n\r\n")
|
discard lnet.net_send(fd, "GET / HTTP/1.0\r\nHost: " & host & "\r\nConnection: close\r\n\r\n")
|
||||||
|
|
||||||
print("Waiting for response...\n")
|
print("Waiting for response...\n")
|
||||||
|
|
||||||
# Simple read loop
|
|
||||||
var total = 0
|
var total = 0
|
||||||
while true:
|
while true:
|
||||||
# lb.pump_membrane_stack()
|
|
||||||
let resp = lnet.net_recv(fd, 512)
|
let resp = lnet.net_recv(fd, 512)
|
||||||
if resp.len > 0:
|
if resp.len > 0:
|
||||||
print(resp)
|
print(resp)
|
||||||
total += resp.len
|
total += resp.len
|
||||||
else:
|
else:
|
||||||
# End of stream or empty poll
|
|
||||||
break
|
break
|
||||||
|
|
||||||
print("\n[HTTP] Closed. Total bytes: " & $total & "\n")
|
print("\n[HTTP] Closed. Total bytes: " & $total & "\n")
|
||||||
lnet.net_close(fd)
|
lnet.net_close(fd)
|
||||||
return @[]
|
return @[]
|
||||||
|
|
||||||
proc cmd_http_serve*(args: seq[string], input: PipelineData): PipelineData =
|
proc cmd_http_serve*(args: seq[string], input: PipelineData): PipelineData =
|
||||||
print("[Server] Starting Nexus Web/1.0...\n")
|
print("[Server] Starting Nexus Web/1.0...\n")
|
||||||
|
|
||||||
let port: uint16 = if args.len > 0: uint16(parseInt(args[0])) else: 80
|
let port: uint16 = if args.len > 0: uint16(parseInt(args[0])) else: 80
|
||||||
|
|
||||||
let s = lb.socket(2, 1, 0)
|
let s = lb.socket(2, 1, 0)
|
||||||
if s < 0:
|
if s < 0:
|
||||||
print("Error: Socket creation failed.\n")
|
print("Error: Socket creation failed.\n")
|
||||||
return @[]
|
return @[]
|
||||||
|
|
||||||
# Bind 0.0.0.0:port
|
# Bind 0.0.0.0:port
|
||||||
var addr_buf: array[16, byte]
|
var addr_buf: array[16, byte]
|
||||||
addr_buf[0] = 2 # AF_INET
|
addr_buf[0] = 2 # AF_INET
|
||||||
copyMem(addr addr_buf[2], unsafeAddr port, 2)
|
copyMem(addr addr_buf[2], unsafeAddr port, 2)
|
||||||
# IP 0.0.0.0 is default 0s
|
# IP 0.0.0.0 is default 0s
|
||||||
|
|
||||||
if lb.bind_socket(s, addr addr_buf[0], 16) < 0:
|
if lb.bind_socket(s, addr addr_buf[0], 16) < 0:
|
||||||
print("Error: Bind failed.\n")
|
print("Error: Bind failed.\n")
|
||||||
return @[]
|
return @[]
|
||||||
|
|
||||||
if lb.listen(s, 1) < 0:
|
if lb.listen(s, 1) < 0:
|
||||||
print("Error: Listen failed.\n")
|
print("Error: Listen failed.\n")
|
||||||
return @[]
|
return @[]
|
||||||
|
|
||||||
print("[Server] Listening on port " & $port & "...\n")
|
print("[Server] Listening on port " & $port & "...\n")
|
||||||
|
|
||||||
while true:
|
while true:
|
||||||
# Accept blocks and pumps stack
|
# Accept blocks and pumps stack
|
||||||
let client = lb.accept(s, nil, nil)
|
let client = lb.accept(s, nil, nil)
|
||||||
if client < 0:
|
if client < 0:
|
||||||
print("Error: Accept failed.\n")
|
print("Error: Accept failed.\n")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
print("[Server] Client Connected (FD " & $client & ")\n")
|
print("[Server] Client Connected (FD " & $client & ")\n")
|
||||||
|
|
||||||
var buf: array[1024, char]
|
var buf: array[1024, char]
|
||||||
let n = lb.recv(client, addr buf[0], 1024, 0)
|
let n = lb.recv(client, addr buf[0], 1024, 0)
|
||||||
|
|
||||||
if n > 0:
|
if n > 0:
|
||||||
var req = ""
|
var req = ""
|
||||||
for i in 0..<n: req.add(buf[i])
|
for i in 0..<n: req.add(buf[i])
|
||||||
print("[Server] Request:\n" & req & "\n")
|
print("[Server] Request:\n" & req & "\n")
|
||||||
|
|
||||||
let resp = "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\nHello from Nexus Unikernel!\n"
|
let resp = "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\nHello from Nexus Unikernel!\n"
|
||||||
discard lb.send(client, unsafeAddr resp[0], uint64(resp.len), 0)
|
discard lb.send(client, unsafeAddr resp[0], uint64(resp.len), 0)
|
||||||
|
|
||||||
discard lb.close(client)
|
discard lb.close(client)
|
||||||
print("[Server] Client Closed.\n")
|
print("[Server] Client Closed.\n")
|
||||||
|
|
||||||
# Just handle one for testing? No, loop forever.
|
# Just handle one for testing? No, loop forever.
|
||||||
# But how to exit? Ctrl-C not implemented in NipBox yet?
|
# But how to exit? Ctrl-C not implemented in NipBox yet?
|
||||||
# We'll just run forever for MVP.
|
# We'll just run forever for MVP.
|
||||||
|
|
||||||
return @[]
|
return @[]
|
||||||
|
|
||||||
proc cmd_from_json*(args: seq[string], input: PipelineData): PipelineData =
|
proc cmd_from_json*(args: seq[string], input: PipelineData): PipelineData =
|
||||||
|
|
@ -774,8 +806,18 @@ proc cmd_set*(args: seq[string], input: PipelineData): PipelineData =
|
||||||
|
|
||||||
|
|
||||||
proc cmd_help*(args: seq[string], input: PipelineData): PipelineData =
|
proc cmd_help*(args: seq[string], input: PipelineData): PipelineData =
|
||||||
print("NipBox " & NIPBOX_VERSION & " (Phase 34: Orbital Drop)\n")
|
when defined(NIPBOX_LITE):
|
||||||
print("Commands: ls, cat, echo, where, http, http.get, http.download, from_json, mount, matrix, set, if, while, help, exit\n")
|
print("NipBox " & NIPBOX_VERSION & " [LITE] (Phase 34: Orbital Drop)\n")
|
||||||
|
print("Commands: ls, cat, cp, mv, touch, edit, echo, where, http, http.get,\n")
|
||||||
|
print(" http.test, http.serve, from_json, mount, matrix, crash,\n")
|
||||||
|
print(" sys.upgrade, set, if, while, help, exit\n")
|
||||||
|
print("Disabled: write, read, http.download (requires SFS)\n")
|
||||||
|
else:
|
||||||
|
print("NipBox " & NIPBOX_VERSION & " (Phase 34: Orbital Drop)\n")
|
||||||
|
print("Commands: ls, cat, cp, mv, touch, edit, echo, where, write, read,\n")
|
||||||
|
print(" http, http.get, http.download, http.test, http.serve,\n")
|
||||||
|
print(" from_json, mount, matrix, crash, sys.upgrade,\n")
|
||||||
|
print(" set, if, while, help, exit\n")
|
||||||
return @[]
|
return @[]
|
||||||
|
|
||||||
# --- DISPATCHER ---
|
# --- DISPATCHER ---
|
||||||
|
|
@ -802,16 +844,16 @@ proc dispatch_command(name: string, args: seq[string],
|
||||||
if args.len < 1:
|
if args.len < 1:
|
||||||
print("Usage: http <ip>\n")
|
print("Usage: http <ip>\n")
|
||||||
return @[]
|
return @[]
|
||||||
|
|
||||||
let host = args[0]
|
let host = args[0]
|
||||||
print("[NipBox] Dialing " & host & ":80...\n")
|
print("[NipBox] Dialing " & host & ":80...\n")
|
||||||
|
|
||||||
# Use libc.socket/connect (Phase 38 Shim)
|
# Use libc.socket/connect (Phase 38 Shim)
|
||||||
let fd = lb.socket(2, 1, 0)
|
let fd = lb.socket(2, 1, 0)
|
||||||
if fd < 0:
|
if fd < 0:
|
||||||
print("Socket Error\n")
|
print("Socket Error\n")
|
||||||
return @[]
|
return @[]
|
||||||
|
|
||||||
# Parse IP (Quick hack for 10.0.2.2)
|
# Parse IP (Quick hack for 10.0.2.2)
|
||||||
# We need proper parsing but let's assume raw IP for MVP
|
# We need proper parsing but let's assume raw IP for MVP
|
||||||
var ip_val: uint32 = 0
|
var ip_val: uint32 = 0
|
||||||
|
|
@ -824,28 +866,28 @@ proc dispatch_command(name: string, args: seq[string],
|
||||||
except:
|
except:
|
||||||
print("Error: Invalid IP format (use A.B.C.D)\n")
|
print("Error: Invalid IP format (use A.B.C.D)\n")
|
||||||
return @[]
|
return @[]
|
||||||
|
|
||||||
# Construct SockAddrIn (Layout must match libc.connect hack)
|
# Construct SockAddrIn (Layout must match libc.connect hack)
|
||||||
var addr_buf: array[16, byte]
|
var addr_buf: array[16, byte]
|
||||||
# Port 80 (0x0050) -> Big Endian 0x0050? No, htons(80) = 0x5000 on LE?
|
# Port 80 (0x0050) -> Big Endian 0x0050? No, htons(80) = 0x5000 on LE?
|
||||||
# 80 = 0x0050. LE in mem: 50 00.
|
# 80 = 0x0050. LE in mem: 50 00.
|
||||||
# LwIP wants host byte order or network?
|
# LwIP wants host byte order or network?
|
||||||
# connect() shim expects us to pass port as uint16.
|
# connect() shim expects us to pass port as uint16.
|
||||||
# But the shim casts addr_ptr+2 to uint16*.
|
# But the shim casts addr_ptr+2 to uint16*.
|
||||||
# If we write 80 there, it reads 80.
|
# If we write 80 there, it reads 80.
|
||||||
|
|
||||||
let port: uint16 = 80
|
let port: uint16 = 80
|
||||||
copyMem(addr addr_buf[2], unsafeAddr port, 2)
|
copyMem(addr addr_buf[2], unsafeAddr port, 2)
|
||||||
copyMem(addr addr_buf[4], unsafeAddr ip_val, 4)
|
copyMem(addr addr_buf[4], unsafeAddr ip_val, 4)
|
||||||
|
|
||||||
if lb.connect(fd, addr addr_buf[0], 16) < 0:
|
if lb.connect(fd, addr addr_buf[0], 16) < 0:
|
||||||
print("Connect Failed\n")
|
print("Connect Failed\n")
|
||||||
return @[]
|
return @[]
|
||||||
|
|
||||||
print("[NipBox] Connected! Sending Payload...\n")
|
print("[NipBox] Connected! Sending Payload...\n")
|
||||||
let req = "GET / HTTP/1.0\r\n\r\n"
|
let req = "GET / HTTP/1.0\r\n\r\n"
|
||||||
discard lb.send(fd, unsafeAddr req[0], uint64(req.len), 0)
|
discard lb.send(fd, unsafeAddr req[0], uint64(req.len), 0)
|
||||||
|
|
||||||
print("[NipBox] Waiting for Data...\n")
|
print("[NipBox] Waiting for Data...\n")
|
||||||
var buf: array[1024, char]
|
var buf: array[1024, char]
|
||||||
while true:
|
while true:
|
||||||
|
|
@ -856,7 +898,7 @@ proc dispatch_command(name: string, args: seq[string],
|
||||||
print(s)
|
print(s)
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
|
|
||||||
print("\n[NipBox] Done.\n")
|
print("\n[NipBox] Done.\n")
|
||||||
return @[]
|
return @[]
|
||||||
of "http.test": return cmd_http_test(args, input)
|
of "http.test": return cmd_http_test(args, input)
|
||||||
|
|
@ -1067,7 +1109,7 @@ proc run_script(path: string) =
|
||||||
proc nipbox_main*() =
|
proc nipbox_main*() =
|
||||||
# DIAGNOSTIC: Very first thing - prove we're executing
|
# DIAGNOSTIC: Very first thing - prove we're executing
|
||||||
print("[NIPBOX] Entry point reached!\n")
|
print("[NIPBOX] Entry point reached!\n")
|
||||||
|
|
||||||
# Phase 30: Pledge Safety
|
# Phase 30: Pledge Safety
|
||||||
# NipBox is the Shell, so it needs broad permissions, but we can restrict RPATH/WPATH to specific zones
|
# NipBox is the Shell, so it needs broad permissions, but we can restrict RPATH/WPATH to specific zones
|
||||||
# For now, we PLEDGE_ALL because the shell needs to explore
|
# For now, we PLEDGE_ALL because the shell needs to explore
|
||||||
|
|
@ -1087,7 +1129,7 @@ proc nipbox_main*() =
|
||||||
# Phase 38: Boot Script
|
# Phase 38: Boot Script
|
||||||
run_script("/init.nsh")
|
run_script("/init.nsh")
|
||||||
print("\x1b[1;32m╚═══════════════════════════════════════╝\x1b[0m\n\n")
|
print("\x1b[1;32m╚═══════════════════════════════════════╝\x1b[0m\n\n")
|
||||||
|
|
||||||
|
|
||||||
print("\x1b[1;33mroot@nexus:# \x1b[0m")
|
print("\x1b[1;33mroot@nexus:# \x1b[0m")
|
||||||
var inputBuffer: string = ""
|
var inputBuffer: string = ""
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue