added seperate input and working on http / sockets
This commit is contained in:
@@ -229,6 +229,23 @@ function toHex(num)
|
||||
return string.format("%X", num)
|
||||
end
|
||||
|
||||
local st={
|
||||
push=function(self,...) table.insert(self.contents,{...}) end,
|
||||
pop=function(self) return table.unpack(table.remove(self.contents)) end,
|
||||
}
|
||||
local q={
|
||||
push=function(self,...) table.insert(self.contents,{...}) end,
|
||||
pop=function(self) return table.unpack(table.remove(self.contents,1)) end,
|
||||
}
|
||||
|
||||
function stack()
|
||||
return setmetatable({contents={}}, {__index=st})
|
||||
end
|
||||
|
||||
function queue()
|
||||
return setmetatable({contents={}}, {__index=q})
|
||||
end
|
||||
|
||||
local function makeSyscallProxy()
|
||||
local backing = {}
|
||||
return setmetatable(backing, {
|
||||
|
||||
@@ -542,6 +542,7 @@ local function allocFD(task)
|
||||
if fd >= kernel.config.maxFilesPerTask then error("ENFILE") end
|
||||
return fd
|
||||
end
|
||||
|
||||
local function checkSystemLimit()
|
||||
if total >= kernel.config.maxOpenFiles - 16 then error("ENFILE") end
|
||||
end
|
||||
@@ -555,27 +556,88 @@ function vfs.newfd(fdobj)
|
||||
return fd
|
||||
end
|
||||
|
||||
function vfs.mount(target, diskOrId)
|
||||
function vfs.mount(target, diskOrId, bind)
|
||||
local _euid = (kernel.currentTask and (kernel.currentTask.euid or kernel.currentTask.uid)) or kernel.uid
|
||||
if _euid ~= 0 then error("EPERM") end
|
||||
if not target then error("EINVAL") end
|
||||
target = normalizeMountPoint(target)
|
||||
local drive, path = resolvePath(target)
|
||||
if not drive:directoryExists(path) then drive:makeDirectory(path) end
|
||||
if drive:type(target) ~= "directory" then error("EINVAL") end
|
||||
|
||||
if not drive:directoryExists(path) then
|
||||
drive:makeDirectory(path)
|
||||
end
|
||||
if drive:type(path) ~= "directory" then
|
||||
error("EINVAL")
|
||||
end
|
||||
local disk, id
|
||||
if type(diskOrId) == "string" then
|
||||
disk = kernel.disks[diskOrId]
|
||||
if not disk then error("ENODEV") end
|
||||
checkDisk(disk); id = diskOrId
|
||||
elseif type(diskOrId) == "table" then
|
||||
checkDisk(diskOrId); disk = diskOrId
|
||||
id = disk.address; vfs.disks[id] = disk
|
||||
else error("EINVAL") end
|
||||
|
||||
-- bind mount
|
||||
if bind then
|
||||
local src = normalizeMountPoint(diskOrId)
|
||||
local srcDrive = resolvePath(src)
|
||||
if not srcDrive then error("ENOENT") end
|
||||
id = "bind:" .. src
|
||||
disk = {
|
||||
address = id,
|
||||
isBind = true,
|
||||
source = srcDrive,
|
||||
exists = function(_, p)
|
||||
return srcDrive:exists(p)
|
||||
end,
|
||||
type = function(_, p)
|
||||
return srcDrive:type(p)
|
||||
end,
|
||||
list = function(_, p)
|
||||
return srcDrive:list(p)
|
||||
end,
|
||||
open = function(_, ...)
|
||||
return srcDrive:open(...)
|
||||
end,
|
||||
remove = function(_, p)
|
||||
return srcDrive:remove(p)
|
||||
end,
|
||||
makeDirectory = function(_, p)
|
||||
return srcDrive:makeDirectory(p)
|
||||
end,
|
||||
rename = function(_, a, b)
|
||||
return srcDrive:rename(a, b)
|
||||
end,
|
||||
size = function(_, p)
|
||||
return srcDrive:size(p)
|
||||
end,
|
||||
lastModified = function(_, p)
|
||||
return srcDrive:lastModified(p)
|
||||
end,
|
||||
isReadOnly = function()
|
||||
return srcDrive:isReadOnly()
|
||||
end,
|
||||
spaceTotal = function()
|
||||
return srcDrive:spaceTotal()
|
||||
end,
|
||||
spaceUsed = function()
|
||||
return srcDrive:spaceUsed()
|
||||
end
|
||||
}
|
||||
vfs.disks[id] = disk
|
||||
else
|
||||
if type(diskOrId) == "string" then
|
||||
disk = vfs.disks[diskOrId]
|
||||
if not disk then error("ENODEV") end
|
||||
checkDisk(disk)
|
||||
id = diskOrId
|
||||
elseif type(diskOrId) == "table" then
|
||||
checkDisk(diskOrId)
|
||||
disk = diskOrId
|
||||
id = disk.address
|
||||
vfs.disks[id] = disk
|
||||
else
|
||||
error("EINVAL")
|
||||
end
|
||||
end
|
||||
if vfs.mounts[id] then error("EBUSY") end
|
||||
for _, mp in pairs(vfs.mounts) do if mp == target then error("EBUSY") end end
|
||||
for _, mp in pairs(vfs.mounts) do
|
||||
if mp == target then
|
||||
error("EBUSY")
|
||||
end
|
||||
end
|
||||
vfs.mounts[id] = target
|
||||
return true
|
||||
end
|
||||
@@ -588,7 +650,11 @@ function vfs.umount(target)
|
||||
for id, mp in pairs(vfs.mounts) do
|
||||
if mp == target then
|
||||
if id == "$" then error("EBUSY") end
|
||||
vfs.mounts[id] = nil; return true
|
||||
vfs.mounts[id] = nil
|
||||
if vfs.disks[id] and vfs.disks[id].isBind then
|
||||
vfs.disks[id] = nil
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
error("EINVAL")
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
--:Minify:--
|
||||
local kernel = ...
|
||||
local cache = {}
|
||||
kernel.searchpaths = {
|
||||
"?", "?.lua", "/lib/?", "/lib/?.lua", "/usr/lib/?", "/usr/lib/?.lua",
|
||||
"/usr/local/lib/?", "/usr/local/lib/?.lua"
|
||||
}
|
||||
kernel.reqcache = cache
|
||||
|
||||
local function require(module, ...)
|
||||
if cache[module] then return cache[module].ret end
|
||||
kernel.currentTask.status = "D"
|
||||
local args = {...}
|
||||
kernel.currentTask.ksh = coroutine.create(function()
|
||||
local coro = function()
|
||||
for _, path in ipairs(kernel.searchpaths) do
|
||||
local filepath = path:gsub("?", module)
|
||||
if kernel.vfs.exists(filepath) then
|
||||
if kernel.vfs.type(filepath) == "directory" then
|
||||
filepath = filepath .. "/init.lua"
|
||||
if kernel.vfs.type(filepath) == "directory" then
|
||||
kernel.asyncReturn(false, "Module not found: "..module)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local fd = kernel.vfs.open(filepath, "r")
|
||||
local chunks = {}
|
||||
while true do
|
||||
local chunk = kernel.vfs.read(fd, 4096)
|
||||
if not chunk or chunk == "" then break end
|
||||
chunks[#chunks + 1] = chunk
|
||||
coroutine.yield()
|
||||
end
|
||||
kernel.vfs.close(fd)
|
||||
local data = table.concat(chunks)
|
||||
local func, err = load(data, "@"..module, "t", kernel._U)
|
||||
if not func then
|
||||
kernel.asyncReturn(false, "Error loading module "..module..": "..tostring(err))
|
||||
return
|
||||
end
|
||||
local ok, ret = xpcall(func, debug.traceback, table.unpack(args))
|
||||
if not ok then
|
||||
kernel.asyncReturn(false, "Error running module "..module..": "..tostring(ret))
|
||||
return
|
||||
end
|
||||
cache[module] = {ret = ret, expires = kernel.EFI:getEpochMs() + 120000}
|
||||
kernel.asyncReturn(true, ret)
|
||||
return
|
||||
end
|
||||
coroutine.yield()
|
||||
end
|
||||
kernel.asyncReturn(false, "Module not found: "..module)
|
||||
end
|
||||
local status, err = xpcall(coro, debug.traceback)
|
||||
if not status then
|
||||
kernel.asyncReturn(false, "Error in require coroutine for module "..module..": "..tostring(err))
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
kernel.syscalls["require"] = require
|
||||
_G.require = function(...) return syscall.require(...) end
|
||||
@@ -1,40 +0,0 @@
|
||||
--:Minify:--
|
||||
local kernel = ...
|
||||
local cache = {}
|
||||
kernel.searchpaths = {
|
||||
"/lib/?.lua", "/lib/?", "/usr/lib/?.lua", "/usr/lib/?",
|
||||
"/usr/local/lib/?.lua", "/usr/local/lib/?", "?.lua", "?"
|
||||
}
|
||||
|
||||
function require(module, ...)
|
||||
if cache[module] then return cache[module] end
|
||||
local modpath = module:gsub("%.", "/")
|
||||
local failed = {}
|
||||
for _, path in ipairs(kernel.searchpaths) do
|
||||
local full_path = string.replace(path, "?", modpath)
|
||||
if full_path:sub(1, 1) ~= "/" then
|
||||
full_path = kernel.currentTask.cwd .. full_path
|
||||
end
|
||||
|
||||
if kernel.vfs.exists(full_path) then
|
||||
if kernel.vfs.type(full_path) == "directory" then
|
||||
full_path = full_path .. "/init"
|
||||
end
|
||||
|
||||
if kernel.vfs.exists(full_path) then
|
||||
local handle = kernel.vfs.open(full_path, "r")
|
||||
local file_content = kernel.vfs.read(handle, 1024 * 1024 * 4)
|
||||
kernel.vfs.close(handle)
|
||||
|
||||
return
|
||||
assert(load(file_content, full_path, "t", kernel._U))(...)
|
||||
else
|
||||
table.insert(failed, full_path)
|
||||
end
|
||||
else
|
||||
table.insert(failed, full_path)
|
||||
end
|
||||
end
|
||||
|
||||
error("Module not found: " .. module .. " (searched paths: " .. table.concat(failed, ", ") .. ")")
|
||||
end
|
||||
@@ -140,6 +140,83 @@ function data.zero(op, mode)
|
||||
end
|
||||
end
|
||||
|
||||
local function buildMeta(entries, opts)
|
||||
opts = opts or {}
|
||||
local uid = opts.uid or 0
|
||||
local gid = opts.gid or 0
|
||||
local perms = opts.perms or 0x3F -- default read/write for owner/group/world
|
||||
|
||||
local chunks = {}
|
||||
table.insert(chunks, string.char(0x02)) -- version header
|
||||
|
||||
for path, target in pairs(entries) do
|
||||
local name = path
|
||||
local nameLen = #name
|
||||
if nameLen > 255 then
|
||||
error("Filename too long (>255 bytes): "..name)
|
||||
end
|
||||
|
||||
-- Determine entry type: 0x01 = symlink if target ~= nil and target ~= ""
|
||||
local entryType = 0x00
|
||||
local cmeta = ""
|
||||
if target and target ~= "" then
|
||||
entryType = 0x01
|
||||
cmeta = target
|
||||
end
|
||||
local cmetaLen = #cmeta
|
||||
if cmetaLen > 255 then
|
||||
error("cmeta too long (>255 bytes) for "..name)
|
||||
end
|
||||
|
||||
-- Build entry as bytes
|
||||
table.insert(chunks, string.char(nameLen)) -- name length
|
||||
table.insert(chunks, name) -- name
|
||||
table.insert(chunks, string.char(entryType)) -- entry type
|
||||
table.insert(chunks, string.char(uid % 256, math.floor(uid/256) % 256)) -- uid
|
||||
table.insert(chunks, string.char(gid % 256, math.floor(gid/256) % 256)) -- gid
|
||||
table.insert(chunks, string.char(perms % 256, math.floor(perms/256) % 256)) -- perms
|
||||
table.insert(chunks, string.char(cmetaLen)) -- cmeta length
|
||||
if cmetaLen > 0 then
|
||||
table.insert(chunks, cmeta)
|
||||
end
|
||||
end
|
||||
|
||||
return table.concat(chunks)
|
||||
end
|
||||
|
||||
local function simpleFile(r,w)
|
||||
return function(op, mode)
|
||||
if op=="type" then
|
||||
return "file"
|
||||
elseif op=="open" then
|
||||
if mode=="r" then
|
||||
return {
|
||||
read=r
|
||||
}
|
||||
elseif mode=="w" then
|
||||
return {
|
||||
write=w
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function strFile(str)
|
||||
local dat=tostring(str)
|
||||
local pos=1
|
||||
return simpleFile(function(amount)
|
||||
pos=pos+amount
|
||||
return dat:sub(pos-amount, pos)
|
||||
end,function() error("EACCES") end)
|
||||
end
|
||||
|
||||
data[".meta"]=strFile(buildMeta({
|
||||
stdin="/proc/self/fd/0",
|
||||
stdout="/proc/self/fd/1",
|
||||
log="/var/log/syslog.log"
|
||||
}))
|
||||
|
||||
if kernel.EFI:getEEPROM() then
|
||||
function data.eeprom(op, mode)
|
||||
if op=="type" then
|
||||
@@ -216,7 +293,8 @@ if kernel.EFI:getNvram() then
|
||||
end
|
||||
end
|
||||
|
||||
data["disk"]={}
|
||||
data["disk"]={["by-id"]={}, ["by-path"]={}, ["by-label"]={}}
|
||||
data["input"]={}
|
||||
kernel.devfs={}
|
||||
kernel.devfs.data=data
|
||||
kernel.devfs.proxy=proxy
|
||||
|
||||
@@ -114,7 +114,8 @@ local function newtaskproxy(task)
|
||||
},
|
||||
children={
|
||||
[".meta"]=strFile(buildMeta(children))
|
||||
}
|
||||
},
|
||||
pid=strFile(task.pid)
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
@@ -1,134 +1,141 @@
|
||||
--:Minify:--
|
||||
local kernel = ...
|
||||
|
||||
local proxy = {}
|
||||
local data = {}
|
||||
kernel.vfs.remove("/tmp")
|
||||
kernel.vfs.mkdir("/tmp")
|
||||
if not kernel.config.tmpToDisk then
|
||||
local proxy = {}
|
||||
local data = {}
|
||||
|
||||
proxy.address = "tmpfs0000"
|
||||
proxy.isvirt = true
|
||||
proxy.isReadOnly = function() return false end
|
||||
proxy.address = "tmpfs0000"
|
||||
proxy.isvirt = true
|
||||
proxy.isReadOnly = function() return false end
|
||||
|
||||
proxy.spaceUsed = function() return 0 end
|
||||
proxy.spaceTotal = function() return 0 end
|
||||
proxy.spaceUsed = function() return 0 end
|
||||
proxy.spaceTotal = function() return 0 end
|
||||
|
||||
proxy.makeDirectory = function(_, path)
|
||||
local steps = kernel.vfs.splitPath(path)
|
||||
local step = data
|
||||
for i=1,#steps do
|
||||
if not step[steps[i]] then
|
||||
step[steps[i]] = {}
|
||||
elseif type(step[steps[i]]) ~= "table" then
|
||||
error("ENOTDIR")
|
||||
proxy.makeDirectory = function(_, path)
|
||||
local steps = kernel.vfs.splitPath(path)
|
||||
local step = data
|
||||
for i=1,#steps do
|
||||
if not step[steps[i]] then
|
||||
step[steps[i]] = {}
|
||||
elseif type(step[steps[i]]) ~= "table" then
|
||||
error("ENOTDIR")
|
||||
end
|
||||
step = step[steps[i]]
|
||||
end
|
||||
step = step[steps[i]]
|
||||
end
|
||||
end
|
||||
|
||||
proxy.remove = function(_, path)
|
||||
local steps = kernel.vfs.splitPath(path)
|
||||
local step = data
|
||||
for i=1,#steps-1 do
|
||||
step = step[steps[i]]
|
||||
if not step then error("ENOENT") end
|
||||
end
|
||||
step[steps[#steps]] = nil
|
||||
end
|
||||
|
||||
proxy.setLabel = function(_, label) end
|
||||
proxy.getLabel = function() return "tmpfs" end
|
||||
|
||||
proxy.attributes = function(_, path)
|
||||
local steps = kernel.vfs.splitPath(path)
|
||||
local step = data
|
||||
for i=1,#steps do
|
||||
step = step[steps[i]]
|
||||
if not step then error("ENOENT") end
|
||||
end
|
||||
return {
|
||||
size = type(step) == "string" and #step or 0,
|
||||
modified = 0,
|
||||
created = 0,
|
||||
}
|
||||
end
|
||||
|
||||
function proxy:open(path, mode)
|
||||
local steps = kernel.vfs.splitPath(path)
|
||||
local step = data
|
||||
for i=1,#steps-1 do
|
||||
if not step[steps[i]] then
|
||||
if mode == "w" then step[steps[i]] = {} else error("ENOENT") end
|
||||
elseif type(step[steps[i]]) ~= "table" then
|
||||
error("ENOTDIR")
|
||||
proxy.remove = function(_, path)
|
||||
local steps = kernel.vfs.splitPath(path)
|
||||
local step = data
|
||||
for i=1,#steps-1 do
|
||||
step = step[steps[i]]
|
||||
if not step then error("ENOENT") end
|
||||
end
|
||||
step = step[steps[i]]
|
||||
step[steps[#steps]] = nil
|
||||
end
|
||||
local filename = steps[#steps]
|
||||
|
||||
if mode == "r" then
|
||||
if type(step[filename]) ~= "string" then error("ENOENT") end
|
||||
local content = step[filename]
|
||||
local pos = 1
|
||||
proxy.setLabel = function(_, label) end
|
||||
proxy.getLabel = function() return "tmpfs" end
|
||||
|
||||
proxy.attributes = function(_, path)
|
||||
local steps = kernel.vfs.splitPath(path)
|
||||
local step = data
|
||||
for i=1,#steps do
|
||||
step = step[steps[i]]
|
||||
if not step then error("ENOENT") end
|
||||
end
|
||||
return {
|
||||
read = function(amount)
|
||||
amount = amount or #content
|
||||
local chunk = content:sub(pos, pos+amount-1)
|
||||
pos = pos + #chunk
|
||||
return chunk
|
||||
end,
|
||||
close = function() end,
|
||||
size = type(step) == "string" and #step or 0,
|
||||
modified = 0,
|
||||
created = 0,
|
||||
}
|
||||
elseif mode == "w" then
|
||||
step[filename] = ""
|
||||
local buf = {}
|
||||
return {
|
||||
write = function(str)
|
||||
buf[#buf + 1] = str
|
||||
end,
|
||||
close = function()
|
||||
step[filename] = table.concat(buf)
|
||||
end,
|
||||
}
|
||||
elseif mode == "a" then
|
||||
if type(step[filename]) ~= "string" then step[filename] = "" end
|
||||
return {
|
||||
write = function(str)
|
||||
step[filename] = step[filename] .. str
|
||||
end,
|
||||
close = function() end,
|
||||
}
|
||||
else
|
||||
error("EACCES")
|
||||
end
|
||||
end
|
||||
|
||||
function proxy:type(path)
|
||||
local steps = kernel.vfs.splitPath(path)
|
||||
local step = data
|
||||
if #steps == 0 then return "directory" end
|
||||
for i=1,#steps do
|
||||
step = step[steps[i]]
|
||||
if not step then return false end
|
||||
function proxy:open(path, mode)
|
||||
local steps = kernel.vfs.splitPath(path)
|
||||
local step = data
|
||||
for i=1,#steps-1 do
|
||||
if not step[steps[i]] then
|
||||
if mode == "w" then step[steps[i]] = {} else error("ENOENT") end
|
||||
elseif type(step[steps[i]]) ~= "table" then
|
||||
error("ENOTDIR")
|
||||
end
|
||||
step = step[steps[i]]
|
||||
end
|
||||
local filename = steps[#steps]
|
||||
|
||||
if mode == "r" then
|
||||
if type(step[filename]) ~= "string" then error("ENOENT") end
|
||||
local content = step[filename]
|
||||
local pos = 1
|
||||
return {
|
||||
read = function(amount)
|
||||
amount = amount or #content
|
||||
local chunk = content:sub(pos, pos+amount-1)
|
||||
pos = pos + #chunk
|
||||
return chunk
|
||||
end,
|
||||
close = function() end,
|
||||
}
|
||||
elseif mode == "w" then
|
||||
step[filename] = ""
|
||||
local buf = {}
|
||||
return {
|
||||
write = function(str)
|
||||
buf[#buf + 1] = str
|
||||
end,
|
||||
close = function()
|
||||
step[filename] = table.concat(buf)
|
||||
end,
|
||||
}
|
||||
elseif mode == "a" then
|
||||
if type(step[filename]) ~= "string" then step[filename] = "" end
|
||||
return {
|
||||
write = function(str)
|
||||
step[filename] = step[filename] .. str
|
||||
end,
|
||||
close = function() end,
|
||||
}
|
||||
else
|
||||
error("EACCES")
|
||||
end
|
||||
end
|
||||
if type(step) == "table" then return "directory" end
|
||||
if type(step) == "string" then return "file" end
|
||||
end
|
||||
|
||||
function proxy:list(path)
|
||||
local steps = kernel.vfs.splitPath(path)
|
||||
local step = data
|
||||
for i=1,#steps do
|
||||
step = step[steps[i]]
|
||||
if not step then error("ENOENT") end
|
||||
function proxy:type(path)
|
||||
local steps = kernel.vfs.splitPath(path)
|
||||
local step = data
|
||||
if #steps == 0 then return "directory" end
|
||||
for i=1,#steps do
|
||||
step = step[steps[i]]
|
||||
if not step then return false end
|
||||
end
|
||||
if type(step) == "table" then return "directory" end
|
||||
if type(step) == "string" then return "file" end
|
||||
end
|
||||
if type(step) ~= "table" then error("ENOTDIR") end
|
||||
local keys = {}
|
||||
for k,_ in pairs(step) do table.insert(keys, k) end
|
||||
return keys
|
||||
end
|
||||
|
||||
function proxy:fileExists(path)
|
||||
local t = self:type(path)
|
||||
return t == "file" or t == "directory"
|
||||
end
|
||||
function proxy:list(path)
|
||||
local steps = kernel.vfs.splitPath(path)
|
||||
local step = data
|
||||
for i=1,#steps do
|
||||
step = step[steps[i]]
|
||||
if not step then error("ENOENT") end
|
||||
end
|
||||
if type(step) ~= "table" then error("ENOTDIR") end
|
||||
local keys = {}
|
||||
for k,_ in pairs(step) do table.insert(keys, k) end
|
||||
return keys
|
||||
end
|
||||
|
||||
kernel.disks["tmpfs0000"] = proxy
|
||||
function proxy:fileExists(path)
|
||||
local t = self:type(path)
|
||||
return t == "file" or t == "directory"
|
||||
end
|
||||
|
||||
kernel.disks["tmpfs0000"] = proxy
|
||||
else
|
||||
kernel.log("tmpToDisk enabled, skipping tmpfs module")
|
||||
kernel.log("tmpfs0000 will passthrough to /tmp on the disk")
|
||||
end
|
||||
@@ -25,10 +25,11 @@ for _, line in ipairs(string.split(kernel.fstab, "\n")) do
|
||||
else
|
||||
local id = line:sub(3, semicolon_pos - 1)
|
||||
local path = trim(line:sub(semicolon_pos + 1))
|
||||
kernel.log("Mounted "..id.." to "..path)
|
||||
kernel.log("Mounting '"..id.."' to '"..path.."'")
|
||||
if id ~= "$" then
|
||||
kernel.vfs.mount(path, id)
|
||||
end
|
||||
kernel.log("Mounted "..id.." to "..path)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,67 +1,194 @@
|
||||
--:Minify:--
|
||||
-- Supports:
|
||||
-- AF_UNIX - local IPC via /var/run/*.sock paths
|
||||
-- AF_INET - network sockets with three backends:
|
||||
-- Implemented by drivers but expect http:// and https://
|
||||
--
|
||||
-- Socket lifecycle:
|
||||
-- fd = syscall.socket(domain, socktype) -- "unix"/"inet", "stream"/"dgram"
|
||||
-- syscall.bind(fd, address) -- server: claim address
|
||||
-- syscall.listen(fd, backlog) -- server: mark as listening
|
||||
-- cfd = syscall.accept(fd) -- server: get connected client fd (blocking poll)
|
||||
-- syscall.connect(fd, address) -- client: connect to server
|
||||
-- syscall.send(fd, data) -- send bytes
|
||||
-- syscall.recv(fd, len) -- receive bytes (blocking poll, returns "" on nothing)
|
||||
-- syscall.sockshutdown(fd) -- half-close send side
|
||||
-- -- normal vfs.close(fd) closes the socket
|
||||
local kernel = ...
|
||||
|
||||
local kernel=...
|
||||
local socket={}
|
||||
socket.handlers={}
|
||||
kernel.socket=socket
|
||||
local socket = {}
|
||||
|
||||
function socket.registerProtocal(protocal, handler)
|
||||
socket.handlers[protocal] = handler
|
||||
socket.handlers = {}
|
||||
|
||||
kernel.socket = socket
|
||||
|
||||
local P = kernel.vfs.P
|
||||
local sys = kernel.syscalls
|
||||
|
||||
function socket.registerProtocal(proto, handler)
|
||||
socket.handlers[proto] = handler
|
||||
end
|
||||
|
||||
function socket.socket()
|
||||
local P=kernel.vfs.P
|
||||
local data=kernel.newFifo()
|
||||
local isClosed=false
|
||||
return kernel.vfs.newfd({
|
||||
handle={
|
||||
read=function() if isClosed then error("ECCON") end return data.pop() end,
|
||||
write=function(data) if isClosed then error("ECCON") end return data.push(data) end,
|
||||
close=function() isClosed = true end
|
||||
local function getHandler(address)
|
||||
for proto, handler in pairs(socket.handlers) do
|
||||
if string.hasPrefix(address, proto) then
|
||||
return handler
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function socket.socket(domain, socktype)
|
||||
local fd = kernel.vfs.newfd({
|
||||
handle = {},
|
||||
|
||||
type = "socket",
|
||||
|
||||
refcount = 1,
|
||||
|
||||
socket = {
|
||||
connected = false,
|
||||
protocol = nil,
|
||||
address = nil
|
||||
},
|
||||
type="socket",
|
||||
refcount=1,
|
||||
socket={},
|
||||
meta={
|
||||
owner=kernel.currentTask.uid,
|
||||
group=kernel.currentTask.uid,
|
||||
etype=2,
|
||||
perms=P.OWNER_R+P.OWNER_W+P.GROUP_R+P.GROUP_W
|
||||
|
||||
meta = {
|
||||
owner = kernel.currentTask.uid,
|
||||
group = kernel.currentTask.uid,
|
||||
etype = 2,
|
||||
|
||||
perms =
|
||||
P.OWNER_R +
|
||||
P.OWNER_W +
|
||||
P.GROUP_R +
|
||||
P.GROUP_W
|
||||
},
|
||||
isvirt=true
|
||||
|
||||
isvirt = true
|
||||
})
|
||||
|
||||
local fdo = kernel.currentTask.fd[fd]
|
||||
|
||||
fdo.handle.read = function()
|
||||
return ""
|
||||
end
|
||||
|
||||
fdo.handle.write = function()
|
||||
return nil, "ENOTCONN"
|
||||
end
|
||||
|
||||
fdo.handle.close = function()
|
||||
fdo.socket.connected = false
|
||||
return true
|
||||
end
|
||||
|
||||
return fd
|
||||
end
|
||||
|
||||
function socket.connect(fd, address)
|
||||
local handler
|
||||
for k, v in pairs(socket.handlers) do
|
||||
if string.hasPrefix(address, k) then
|
||||
handler=v
|
||||
end
|
||||
local fdo = kernel.currentTask.fd[fd]
|
||||
|
||||
if not fdo then
|
||||
return nil, "EBADF"
|
||||
end
|
||||
handler.connect(kernel.currentTask.fd[fd], address)
|
||||
|
||||
if fdo.type ~= "socket" then
|
||||
return nil, "ENOTSOCK"
|
||||
end
|
||||
|
||||
local handler =
|
||||
getHandler(address)
|
||||
|
||||
if not handler then
|
||||
return nil, "EPROTONOSUPPORT"
|
||||
end
|
||||
|
||||
fdo.socket.protocol = handler
|
||||
fdo.socket.address = address
|
||||
|
||||
local ok, err =
|
||||
handler.connect(fd, address)
|
||||
|
||||
if not ok then
|
||||
return nil, err
|
||||
end
|
||||
|
||||
fdo.socket.connected = true
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function socket.listen(fd, backlog)
|
||||
error("Not Implemented")
|
||||
local fdo =
|
||||
kernel.currentTask.fd[fd]
|
||||
|
||||
if not fdo then
|
||||
return nil, "EBADF"
|
||||
end
|
||||
|
||||
if not fdo.handle.listen then
|
||||
return nil, "EOPNOTSUPP"
|
||||
end
|
||||
|
||||
return fdo.handle.listen(backlog)
|
||||
end
|
||||
|
||||
local sys=kernel.syscalls
|
||||
sys["socket"]=socket.socket
|
||||
function socket.send(fd, data)
|
||||
local fdo =
|
||||
kernel.currentTask.fd[fd]
|
||||
|
||||
if not fdo then
|
||||
return nil, "EBADF"
|
||||
end
|
||||
|
||||
if fdo.type ~= "socket" then
|
||||
return nil, "ENOTSOCK"
|
||||
end
|
||||
|
||||
if not fdo.socket.connected then
|
||||
return nil, "ENOTCONN"
|
||||
end
|
||||
|
||||
if not fdo.handle.write then
|
||||
return nil, "EOPNOTSUPP"
|
||||
end
|
||||
|
||||
return fdo.handle.write(data)
|
||||
end
|
||||
|
||||
function socket.recv(fd, amount)
|
||||
local fdo =
|
||||
kernel.currentTask.fd[fd]
|
||||
|
||||
if not fdo then
|
||||
return nil, "EBADF"
|
||||
end
|
||||
|
||||
if fdo.type ~= "socket" then
|
||||
return nil, "ENOTSOCK"
|
||||
end
|
||||
|
||||
if not fdo.socket.connected then
|
||||
return nil, "ENOTCONN"
|
||||
end
|
||||
|
||||
if not fdo.handle.read then
|
||||
return nil, "EOPNOTSUPP"
|
||||
end
|
||||
|
||||
return fdo.handle.read(amount)
|
||||
end
|
||||
|
||||
function socket.sockshutdown(fd)
|
||||
local fdo =
|
||||
kernel.currentTask.fd[fd]
|
||||
|
||||
if not fdo then
|
||||
return nil, "EBADF"
|
||||
end
|
||||
|
||||
if fdo.type ~= "socket" then
|
||||
return nil, "ENOTSOCK"
|
||||
end
|
||||
|
||||
if fdo.handle.close then
|
||||
return fdo.handle.close()
|
||||
end
|
||||
|
||||
fdo.socket.connected = false
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
sys.socket = socket.socket
|
||||
sys.connect = socket.connect
|
||||
sys.listen = socket.listen
|
||||
sys.send = socket.send
|
||||
sys.recv = socket.recv
|
||||
sys.sockshutdown = socket.sockshutdown
|
||||
|
||||
kernel.log("Loaded socket module")
|
||||
@@ -0,0 +1,129 @@
|
||||
--:Minify:--
|
||||
local kernel = ...
|
||||
|
||||
local vterms = {}
|
||||
|
||||
local function createVt(id, width, height)
|
||||
local vt = {
|
||||
id = id,
|
||||
width = width,
|
||||
height = height,
|
||||
buffer = {},
|
||||
cursorX = 1,
|
||||
cursorY = 1,
|
||||
fgColor = 0xFFFFFF,
|
||||
bgColor = 0x000000,
|
||||
obj = {}
|
||||
}
|
||||
|
||||
for y = 1, height do
|
||||
vt.buffer[y] = {}
|
||||
for x = 1, width do
|
||||
vt.buffer[y][x] = {char = " ", fgColor = vt.fgColor, bgColor = vt.bgColor}
|
||||
end
|
||||
end
|
||||
|
||||
local function scroll(lines)
|
||||
for _ = 1, lines do
|
||||
table.remove(vt.buffer, 1)
|
||||
vt.buffer[vt.height] = {}
|
||||
for x = 1, vt.width do
|
||||
vt.buffer[vt.height][x] = {char = " ", fgColor = vt.fgColor, bgColor = vt.bgColor}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function vt.obj:write(content)
|
||||
local x, y = vt.cursorX, vt.cursorY
|
||||
if x>vt.width then return end
|
||||
if y>vt.height then return end
|
||||
for i = 1, #content do
|
||||
local c = content:sub(i, i)
|
||||
if c == "\n" then
|
||||
y = y + 1
|
||||
x = 1
|
||||
elseif c == "\t" then
|
||||
local tabSize = 4
|
||||
local spaces = tabSize - ((x - 1) % tabSize)
|
||||
for _ = 1, spaces do
|
||||
vt.buffer[y][x] = {char = " ", fgColor = vt.fgColor, bgColor = vt.bgColor}
|
||||
x = x + 1
|
||||
if x > vt.width then
|
||||
x = 1
|
||||
y = y + 1
|
||||
end
|
||||
end
|
||||
elseif c == "\b" then
|
||||
if x > 1 then
|
||||
x = x - 1
|
||||
vt.buffer[y][x] = {char = " ", fgColor = vt.fgColor, bgColor = vt.bgColor}
|
||||
end
|
||||
else
|
||||
if x <= vt.width and y <= vt.height then
|
||||
vt.buffer[y][x] = {char = c, fgColor = vt.fgColor, bgColor = vt.bgColor}
|
||||
x = x + 1
|
||||
end
|
||||
end
|
||||
|
||||
if x > vt.width then
|
||||
x = 1
|
||||
y = y + 1
|
||||
end
|
||||
|
||||
if y > vt.height then
|
||||
scroll(1)
|
||||
y = vt.height
|
||||
end
|
||||
end
|
||||
|
||||
vt.cursorX, vt.cursorY = x, y
|
||||
end
|
||||
|
||||
function vt.obj:spos(x, y)
|
||||
vt.cursorX = tonumber(x)
|
||||
vt.cursorY = tonumber(y)
|
||||
end
|
||||
|
||||
function vt.obj:gpos()
|
||||
return tostring(vt.cursorX)..";"..tostring(vt.cursorY)
|
||||
end
|
||||
|
||||
function vt.obj:sfgc(color)
|
||||
vt.fgColor = tonumber(color)
|
||||
end
|
||||
|
||||
function vt.obj:sbgc(color)
|
||||
vt.bgColor = tonumber(color)
|
||||
end
|
||||
|
||||
function vt.obj:gfgc()
|
||||
return vt.fgColor
|
||||
end
|
||||
|
||||
function vt.obj:gbgc()
|
||||
return vt.bgColor
|
||||
end
|
||||
|
||||
function vt.obj:gplt()
|
||||
return 24
|
||||
end
|
||||
|
||||
function vt.obj:clear()
|
||||
for y = 1, vt.height do
|
||||
for x = 1, vt.width do
|
||||
vt.buffer[y][x] = {char = " ", fgColor = vt.fgColor, bgColor = vt.bgColor}
|
||||
end
|
||||
end
|
||||
vt.cursorX, vt.cursorY = 1, 1
|
||||
end
|
||||
|
||||
function vt.obj:isvirt()
|
||||
return true
|
||||
end
|
||||
|
||||
function vt.obj:gctrl()
|
||||
return serializeBool(vt.ctrl)..";"..serializeBool(vt.alt)
|
||||
end
|
||||
|
||||
return vt
|
||||
end
|
||||
@@ -21,7 +21,7 @@ local function loadExecutable(path)
|
||||
local env = kernel.freshUserEnv()
|
||||
|
||||
local func, err = load(data, "@" .. path, "t", env)
|
||||
if not func then error("ENOEXEC: " .. tostring(err)) end
|
||||
if not func then kernel.log("Failed to load executable: " .. tostring(err).. " : ".. tostring(path)); error("ENOEXEC: " .. tostring(err)) end
|
||||
|
||||
local meta = kernel.vfs.lstat(path)
|
||||
local suid_set = bit_is_set(meta.perms, 6)
|
||||
@@ -211,6 +211,8 @@ function sys.kill(pid, force)
|
||||
return false, "Task is already dead"
|
||||
elseif task.status == "D" and not force then
|
||||
return false, "Cannot kill task waiting for IO"
|
||||
elseif task.euid ~= kernel.uid and kernel.uid ~= 0 then
|
||||
return false, "Different user"
|
||||
end
|
||||
local caller = kernel.currentTask
|
||||
local ceuid = caller and (caller.euid or caller.uid) or kernel.uid
|
||||
@@ -227,6 +229,8 @@ function sys.stop(pid)
|
||||
return false, "Task does not exist"
|
||||
elseif task.status ~= "R" and task.status ~= "S" then
|
||||
return false, "Cannot stop non-running task"
|
||||
elseif task.euid ~= kernel.uid and kernel.uid ~= 0 then
|
||||
return false, "Different user"
|
||||
else
|
||||
if task.status == "S" then
|
||||
task.status = "ST"
|
||||
@@ -243,6 +247,8 @@ function sys.continue(pid)
|
||||
return false, "Task does not exist"
|
||||
elseif task.status ~= "T" and task.status ~= "ST" then
|
||||
return false, "Task is not stopped"
|
||||
elseif task.euid ~= kernel.uid and kernel.uid ~= 0 then
|
||||
return false, "Different user"
|
||||
else
|
||||
if task.status == "ST" then
|
||||
task.status = "S"
|
||||
@@ -299,20 +305,21 @@ function sys.getuid() return kernel.currentTask.uid end
|
||||
local function reapDeadTasks()
|
||||
for pid, task in pairs(tasks) do
|
||||
if task.status == "Z" and not task.reapTime then
|
||||
task.coro = nil
|
||||
task.ivs = nil
|
||||
task.vs = nil
|
||||
task.args = nil
|
||||
task.envars = nil
|
||||
task.cwd = nil
|
||||
task.numRuns = nil
|
||||
task.totalTime = nil
|
||||
task.lastTime = nil
|
||||
task.timeSlice = nil
|
||||
if task.pid == 1 then kernel.panic("Attempted to gc init!") end
|
||||
task.coro = nil
|
||||
task.ivs = nil
|
||||
task.vs = nil
|
||||
task.args = nil
|
||||
task.envars = nil
|
||||
task.cwd = nil
|
||||
task.numRuns = nil
|
||||
task.totalTime = nil
|
||||
task.lastTime = nil
|
||||
task.timeSlice = nil
|
||||
task.syscallReturn = nil
|
||||
task.sleep = nil
|
||||
task.fd = nil
|
||||
task.reapTime = kernel.EFI:getEpochMs() + 30000
|
||||
task.sleep = nil
|
||||
task.fd = nil
|
||||
task.reapTime = kernel.EFI:getEpochMs() + 30000
|
||||
|
||||
elseif task.reapTime and kernel.EFI:getEpochMs() > task.reapTime
|
||||
and task.status == "Z" then
|
||||
@@ -344,7 +351,23 @@ local k_max = 0.5
|
||||
local B = 0.01
|
||||
|
||||
function kernel.main()
|
||||
kernel.log("Starting main loop...")
|
||||
kernel.saveLog()
|
||||
local stopLog=5
|
||||
local logTasks=100
|
||||
|
||||
while not kernel.exitMain do
|
||||
if kernel.config.logTasks then
|
||||
if logTasks<0 then
|
||||
kernel.log("Active Tasks:")
|
||||
for i,v in pairs(tasks) do
|
||||
kernel.log(v.name.." : "..v.status.." : "..tostring(v.pid).." : "..tostring(v.exit))
|
||||
end
|
||||
kernel.log("[END BLOCK]")
|
||||
logTasks=100
|
||||
end
|
||||
logTasks=logTasks-1
|
||||
end
|
||||
local N = 0
|
||||
local Tmin_hit = 0
|
||||
local Tmax_hit = 0
|
||||
@@ -352,6 +375,7 @@ function kernel.main()
|
||||
local taskTimes = {}
|
||||
|
||||
for pid, task in pairs(tasks) do
|
||||
if kernel.exitMain then break end
|
||||
kernel.currentTask = task
|
||||
kernel.uid = task.euid or task.uid
|
||||
kernel.process = task.name
|
||||
@@ -366,9 +390,29 @@ function kernel.main()
|
||||
task.sleep = 0
|
||||
end
|
||||
|
||||
if task.status == "DS" and kernel.EFI:getEpochMs() >= task.sleep then
|
||||
task.status = "D"
|
||||
task.sleep = 0
|
||||
end
|
||||
|
||||
if task.status == "D" then
|
||||
if task.ksh then
|
||||
coroutine.resume(task.ksh)
|
||||
if coroutine.status(task.ksh) == "dead" then
|
||||
task.ksh = nil
|
||||
if task.status == "D" then
|
||||
task.status = "R"
|
||||
end
|
||||
|
||||
if kernel.config.debugSyscalls then
|
||||
kernel.log("Task " .. task.pid .. " IO wait completed", "DBUG", 0x00FFFF)
|
||||
local sysret = task.syscallReturn
|
||||
for i = 2, #sysret do
|
||||
local v = type(sysret[i]) == "table" and table.serialize(sysret[i]) or tostring(sysret[i])
|
||||
kernel.log(" retval[" .. (i-1) .. "] = " .. v, "DBUG", 0x00FFFF)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -405,7 +449,9 @@ function kernel.main()
|
||||
if task.status == "R" then
|
||||
local startTime = kernel.EFI:getEpochMs()
|
||||
local ret
|
||||
|
||||
if stopLog > 0 then
|
||||
kernel.log("Running task " .. tostring(task.pid) .. " (" .. task.name .. ") with time slice " .. string.format("%.3f", task.timeSlice) .. "s", "DBUG", 0x00FFFF)
|
||||
end
|
||||
if kernel.config.preempt then
|
||||
if not task.debugger then
|
||||
ret = { resumeWithTimeout(task.coro, task.timeSlice, table.unpack(task.syscallReturn)) }
|
||||
@@ -491,6 +537,11 @@ function kernel.main()
|
||||
end
|
||||
|
||||
reapDeadTasks()
|
||||
if stopLog > 0 then
|
||||
kernel.log("Executed " .. N .. " tasks, avg time: " .. string.format("%.2f", T_prev_avg) .. "ms, var: " .. string.format("%.2f", T_prev_var) .. ", B: " .. string.format("%.5f", B))
|
||||
stopLog = stopLog - 1
|
||||
kernel.saveLog()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
local kernel = ...
|
||||
kernel.processes.kgc = function()
|
||||
while true do
|
||||
for i,v in pairs(kernel.reqcache) do
|
||||
if v.expires and kernel.EFI:getEpochMs() > v.expires then
|
||||
kernel.reqcache[i] = nil
|
||||
end
|
||||
end
|
||||
kernel.sleep(5000)
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user