1
1
mirror of https://git.shadowkat.net/izaya/OC-PsychOS2.git synced 2024-11-22 03:54:20 -05:00

Compare commits

...

3 Commits

Author SHA1 Message Date
XeonSquared
9022bfce78 accidentally delet'd the wrong file 2019-10-28 00:35:32 +11:00
XeonSquared
aaccc053d3 cursed 2019-10-27 23:34:02 +11:00
XeonSquared
824c443629 fuck this shit 2019-10-27 02:40:25 +11:00
19 changed files with 436 additions and 262 deletions

8
exec/init.lua Normal file
View File

@ -0,0 +1,8 @@
xpcall(function()
io.input("/dev/tty0")
io.output("/dev/tty0")
os.setenv("PWD","/boot")
io.write("PsychOS v2.0a1 - ")
print(tostring(math.floor(computer.totalMemory()/1024)).."K RAM")
os.spawnfile("/boot/exec/shell.lua")
end,function(e) dprint(e) end,"init test")

View File

@ -1,4 +1,5 @@
print("PID# VTY# Name") print("PID# Parent Name")
for k,v in pairs(tTasks) do for k,v in pairs(os.tasks()) do
print(string.format("%4d %4d %s",k,v.e.t or 0,v.n)) local t = os.taskInfo(v)
print(string.format("%4d %4d %s",k,v.parent,v.name))
end end

171
module/buffer.lua Normal file
View File

@ -0,0 +1,171 @@
buffer = {}
local metatable = {
__index = buffer,
__metatable = "file",
__close = buffer.close
}
function buffer.new(mode, stream)
local result = {
closed = false,
tty = false,
mode = {},
stream = stream,
bufferRead = "",
bufferWrite = "",
bufferSize = math.max(512, math.min(8 * 1024, computer.freeMemory() / 8)),
bufferMode = "full",
readTimeout = math.huge,
}
mode = mode or "r"
for i = 1, mode:len() do
result.mode[mode:sub(i, i)] = true
end
-- when stream closes, result should close first
-- when result closes, stream should close after
-- when stream closes, it is removed from the proc
stream.close = setmetatable({close = stream.close,parent = result},{__call = buffer.close})
return setmetatable(result, metatable)
end
function buffer:close()
-- self is either the buffer, or the stream.close callable
local meta = getmetatable(self)
if meta == metatable.__metatable then
return self.stream:close()
end
local parent = self.parent
if parent.mode.w or parent.mode.a then
parent:flush()
end
parent.closed = true
return self.close(parent.stream)
end
function buffer:flush()
if #self.bufferWrite > 0 then
local tmp = self.bufferWrite
self.bufferWrite = ""
local result, reason = self.stream:write(tmp)
if not result then
return nil, reason or "bad file descriptor"
end
end
return self
end
function buffer:lines(...)
local args = table.pack(...)
return function()
local result = table.pack(self:read(table.unpack(args, 1, args.n)))
if not result[1] and result[2] then
error(result[2])
end
return table.unpack(result, 1, result.n)
end
end
local function readChunk(self)
if computer.uptime() > self.timeout then
error("timeout")
end
local result, reason = self.stream:read(math.max(1,self.bufferSize))
if result then
self.bufferRead = self.bufferRead .. result
return self
else -- error or eof
return result, reason
end
end
function buffer:readLine(chop, timeout)
self.timeout = timeout or (computer.uptime() + self.readTimeout)
local start = 1
while true do
local buf = self.bufferRead
local i = buf:find("[\r\n]", start)
local c = i and buf:sub(i,i)
local is_cr = c == "\r"
if i and (not is_cr or i < #buf) then
local n = buf:sub(i+1,i+1)
if is_cr and n == "\n" then
c = c .. n
end
local result = buf:sub(1, i - 1) .. (chop and "" or c)
self.bufferRead = buf:sub(i + #c)
return result
else
start = #self.bufferRead - (is_cr and 1 or 0)
local result, reason = readChunk(self)
if not result then
if reason then
return result, reason
else -- eof
result = #self.bufferRead > 0 and self.bufferRead or nil
self.bufferRead = ""
return result
end
end
end
coroutine.yield()
end
end
function buffer:read(...)
if not self.mode.r then
return nil, "read mode was not enabled for this stream"
end
if self.mode.w or self.mode.a then
self:flush()
end
if select("#", ...) == 0 then
return self:readLine(true)
end
return self:formatted_read(readChunk, ...)
end
function buffer:setvbuf(mode, size)
mode = mode or self.bufferMode
size = size or self.bufferSize
assert(mode == "no" or mode == "full" or mode == "line",
"bad argument #1 (no, full or line expected, got " .. tostring(mode) .. ")")
assert(mode == "no" or type(size) == "number",
"bad argument #2 (number expected, got " .. type(size) .. ")")
self.bufferMode = mode
self.bufferSize = size
return self.bufferMode, self.bufferSize
end
function buffer:write(...)
if self.closed then
return nil, "bad file descriptor"
end
if not self.mode.w and not self.mode.a then
return nil, "write mode was not enabled for this stream"
end
local args = table.pack(...)
for i = 1, args.n do
if type(args[i]) == "number" then
args[i] = tostring(args[i])
end
checkArg(i, args[i], "string")
end
for i = 1, args.n do
local arg = args[i]
local result, reason
result, reason = self.stream:write(arg)
if not result then
return nil, reason
end
end
return self
end

View File

@ -1,19 +0,0 @@
local ts = {}
for a,_ in component.list("screen") do
ts[#ts+1] = a
end
local ttyn = 0
for a,_ in component.list("gpu") do
local r,w = vtemu(a,table.remove(ts,1))
-- fd[#fd+1] = {["read"]=r,["write"]=w,["close"]=function() w("\27[2J\27[H") end,["t"]="t"}
iofs.register("tty"..tostring(ttyn),function() return r,w,function() w("\27[2J\27[H") end end)
local f = io.open("/iofs/tty"..tostring(ttyn),"rw")
fd[f.fd].t = "t"
ttyn = ttyn + 1
end
do
iofs.register("syslog",function() return function() return "" end, function(msg) syslog(msg,nil,tTasks[cPid].n) end, function() return true end end)
end
if #fd < 1 then
io.open("/iofs/syslog","rw")
end

68
module/devfs.lua Normal file
View File

@ -0,0 +1,68 @@
devfs = {}
devfs.files = {}
devfs.fds = {}
devfs.nextfd = 0
devfs.component = {}
local function rfalse()
return false
end
function devfs.component.getLabel()
return "devfs"
end
devfs.component.spaceUsed, devfs.component.spaceTotal, devfs.component.isReadOnly, devfs.component.isDirectory,devfs.component.size, devfs.component.setLabel = function() return computer.totalMemory()-computer.freeMemory() end, computer.totalMemory, rfalse, rfalse, rfalse, rfalse
function devfs.component.exists(fname)
return devfs.files[fname] ~= nil
end
function devfs.component.list()
local t = {}
for k,v in pairs(devfs.files) do
t[#t+1] = k
end
return t
end
function devfs.component.open(fname, mode)
fname=fname:gsub("/","")
if devfs.files[fname] then
local r,w,c,s = devfs.files[fname](mode)
devfs.fds[devfs.nextfd] = {["read"]=r or rfalse,["write"]=w or rfalse,["seek"]=s or rfalse,["close"]=c or rfalse}
devfs.nextfd = devfs.nextfd + 1
return devfs.nextfd - 1
end
return false
end
function devfs.component.read(fd,count)
if devfs.fds[fd] then
return devfs.fds[fd].read(count)
end
end
function devfs.component.write(fd,data)
if devfs.fds[fd] then
return devfs.fds[fd].write(data)
end
end
function devfs.component.close(fd)
if devfs.fds[fd] then
devfs.fds[fd].close()
end
devfs.fds[fd] = nil
end
function devfs.component.seek(fd,...)
if devfs.fds[fd] then
return devfs.fds[fd].seek(...)
end
end
function devfs.component.remove(fname)
end
function devfs.register(fname,fopen) -- Register a new devfs node with the name *fname* that will run the function *fopen* when opened. This function should return a function for read, a function for write, function for close, and optionally, a function for seek, in that order.
devfs.files[fname] = fopen
end
fs.mounts.dev = devfs.component
--#include "module/devfs/null.lua"

8
module/devfs/cons.lua Normal file
View File

@ -0,0 +1,8 @@
devfs.register("cons",function()
return function()
end,
function()
end,
function()
end
end)

3
module/devfs/null.lua Normal file
View File

@ -0,0 +1,3 @@
devfs.register("null",function()
return function() end, function() end, function() end
end)

2
module/devfs/syslog.lua Normal file
View File

@ -0,0 +1,2 @@
devfs.register("syslog",function()
return function() end, syslog, function() end end)

View File

@ -1,51 +0,0 @@
do
local tG,ttyn = {}, 0
local function checkUnused(addr) -- returns false if a screen *addr* is already allocated to a GPU
for k,v in pairs(tG) do
if v == addr then
return false
end
end
return true
end
local function findNextDisplay() -- finds the next available screen, or nil if there are no available screens
for a,_ in component.list("screen") do
if checkUnused(a) then
return a
end
end
return nil
end
for file in ipairs(fs.list("/boot/cfg/disp/") or {}) do -- allows files in /boot/cfg/disp with filenames as GPU addresses to bind to specific screens
if component.proxy(file) then
local f = io.open("/boot/cfg/disp/"..file)
if f then
local sA = file:read()
if checkUnused(sA) then
tG[file] = sA
end
f:close()
end
end
end
for a,_ in component.list("gpu") do -- allocate a screen to every unused GPU
tG[a] = findNextDisplay()
end
for gpu,screen in pairs(tG) do
local r,w = vtemu(gpu,screen)
iofs.register("tty"..tostring(ttyn),function() return r,w,function() w("\27[2J\27[H") end end)
local f = io.open("/iofs/tty"..tostring(ttyn),"rw")
fd[f.fd].t = "t"
ttyn = ttyn + 1
end
do
iofs.register("syslog",function() return function() return "" end, function(msg) syslog(msg,nil,tTasks[cPid].n) end, function() return true end end)
end
if #fd < 1 then
io.open("/iofs/syslog","rw")
end
end

View File

@ -1,20 +1,12 @@
os.spawn(function() print(pcall(function() --#include "module/syslog.lua"
print(_OSVERSION,tostring(math.floor(computer.totalMemory()/1024)).."K memory") --#include "module/sched.lua"
os.setenv("PWD","/boot") --#include "module/buffer.lua"
local f = fs.open("/boot/init.txt","rb") --#include "module/fs.lua"
if f then --#include "module/io.lua"
local fc = f:read("*a") --#include "module/devfs.lua"
f:close() --#include "module/devfs/syslog.lua"
for line in fc:gmatch("[^\n]+") do --#include "module/loadfile.lua"
print("Starting service "..line) --#include "module/term.lua"
spawnfile("/boot/service/"..line,line) os.spawnfile("/boot/exec/init.lua","init")
end
end os.sched()
for k,v in pairs(fd) do
if v.t == "t" then
os.setenv("t",k)
print("Spawning a shell for terminal #"..tostring(k))
spawnfile("/boot/exec/shell.lua","shell [local:"..tostring(k).."]")
end
end
end)) end,"init")

View File

@ -1,65 +1,116 @@
do do
_G.fd,_G.io = {},{} io = {}
function io.write(d) -- writes *d* to stdout
fd[os.getenv("t") or 1].write(d) function io.close(file)
return (file or io.output()):close()
end end
function io.read(d,b) -- reads *d* from stdin, until something is returned, or b is true
local r = "" function io.flush()
repeat return io.output():flush()
r=fd[os.getenv("t") or 1].read(d)
coroutine.yield()
until r or b
return r
end end
function print(...) -- outputs its arguments to stdout, separated by newlines
for k,v in pairs({...}) do function io.lines(filename, ...)
if filename then
local file, reason = io.open(filename)
if not file then
error(reason, 2)
end
local args = table.pack(...)
return function()
local result = table.pack(file:read(table.unpack(args, 1, args.n)))
if not result[1] then
if result[2] then
error(result[2], 2)
else -- eof
file:close()
return nil
end
end
return table.unpack(result, 1, result.n)
end
else
return io.input():lines()
end
end
function io.open(path, mode)
local stream, result = fs.open(path, mode)
if stream then
return buffer.new(mode, stream)
else
return nil, result
end
end
local fdt = {[0]="STDIN","STDOUT","STDERR"}
local function getfh(fd)
return os.getenv(fdt[fd] or "FILE"..tostring(fd))
end
local function setfh(fd,fh)
os.setenv(fdt[fd] or "FILE"..tostring(fd),fh)
end
function io.stream(fd,file,mode)
checkArg(1,fd,'number')
assert(fd>=0,'fd must be >= 0. 0 is input, 1 is stdout, 2 is stderr')
if file then
if type(file) == "string" then
local result, reason = io.open(file, mode)
if not result then
error(reason, 2)
end
file = result
elseif not io.type(file) then
error("bad argument #1 (string or file expected, got " .. type(file) .. ")", 2)
end
setfh(fd,file)
end
return getfh(fd)
end
function io.input(file)
return io.stream(0, file, 'r')
end
function io.output(file)
return io.stream(1, file,'w')
end
function io.error(file)
return io.stream(2, file,'w')
end
function io.read(...)
return io.input():read(...)
end
function io.tmpfile()
local name = os.tmpname()
if name then
return io.open(name, "a")
end
end
function io.type(object)
if type(object) == "table" then
if getmetatable(object) == "file" then
if object.stream.handle then
return "file"
else
return "closed file"
end
end
end
return nil
end
function io.write(...)
return io.output():write(...)
end
function print(...)
for k,v in ipairs({...}) do
io.write(tostring(v).."\n") io.write(tostring(v).."\n")
end end
end end
local function fdw(f,d)
fd[f.fd].write(d)
end
local function fdr(f,d)
return fd[f.fd].read(d)
end
local function fdc(f)
fd[f.fd].close()
fd[f.fd] = nil
end
function io.newfd() -- creates a new file descriptor and returns it plus its ID
local nfd=#fd+1
fd[nfd] = {}
return nfd,fd[nfd]
end
local function fdfile(f,m) -- create a fd from a file
local e,fobj = pcall(fs.open,f,m)
if e and fobj then
local fdi, fdo =io.newfd()
if fobj.read then
function fdo.read(d)
return fobj:read(d)
end
end
if fobj.write then
function fdo.write(d)
return fobj:write(d)
end
end
function fdo.close()
fobj:close()
end
return fdi
end
return false
end
function io.open(f,m) -- opens file or file descriptor *f* with mode *m*
if type(f) == "string" then
f = fdfile(f,m)
end
if fd[f] then
local t = {["close"]=fdc,["read"]=fdr,["write"]=fdw,["fd"]=f,["mode"]=m}
return t
end
return false
end
end end

View File

@ -1,64 +0,0 @@
iofs = {}
iofs.files = {}
iofs.fds = {}
iofs.nextfd = 0
iofs.component = {}
local function rfalse()
return false
end
function iofs.component.getLabel()
return "iofs"
end
iofs.component.spaceUsed, iofs.component.spaceTotal, iofs.component.isReadOnly, iofs.component.isDirectory,iofs.component.size, iofs.component.setLabel = function() return computer.totalMemory()-computer.freeMemory() end, computer.totalMemory, rfalse, rfalse, rfalse, rfalse
function iofs.component.exists(fname)
return iofs.files[fname] ~= nil
end
function iofs.component.list()
local t = {}
for k,v in pairs(iofs.files) do
t[#t+1] = k
end
return t
end
function iofs.component.open(fname, mode)
fname=fname:gsub("/","")
if iofs.files[fname] then
local r,w,c,s = iofs.files[fname](mode)
iofs.fds[iofs.nextfd] = {["read"]=r or rfalse,["write"]=w or rfalse,["seek"]=s or rfalse,["close"]=c or rfalse}
iofs.nextfd = iofs.nextfd + 1
return iofs.nextfd - 1
end
return false
end
function iofs.component.read(fd,count)
if iofs.fds[fd] then
return iofs.fds[fd].read(count)
end
end
function iofs.component.write(fd,data)
if iofs.fds[fd] then
return iofs.fds[fd].write(data)
end
end
function iofs.component.close(fd)
if iofs.fds[fd] then
iofs.fds[fd].close()
end
iofs.fds[fd] = nil
end
function iofs.component.seek(fd,...)
if iofs.fds[fd] then
return iofs.fds[fd].seek(...)
end
end
function iofs.register(fname,fopen) -- Register a new iofs node with the name *fname* that will run the function *fopen* when opened. This function should return a function for read, a function for write, function for close, and optionally, a function for seek, in that order.
iofs.files[fname] = fopen
end
fs.mounts.iofs = iofs.component

View File

@ -7,8 +7,8 @@ end
function runfile(p,...) -- runs file *p* with arbitrary arguments in the current thread function runfile(p,...) -- runs file *p* with arbitrary arguments in the current thread
return loadfile(p)(...) return loadfile(p)(...)
end end
function spawnfile(p,n) -- spawns a new process from file *p* with name *n* function os.spawnfile(p,n) -- spawns a new process from file *p* with name *n*
return os.spawn(function() print(pcall(loadfile(p))) end,n) return os.spawn(function() xpcall(loadfile(p),function(e) dprint(e.."\n"..debug.traceback) end) end,n)
end end
function require(f) -- searches for a library with name *f* and returns what the library returns, if possible function require(f) -- searches for a library with name *f* and returns what the library returns, if possible
local lib = os.getenv("LIB") or "/boot/lib" local lib = os.getenv("LIB") or "/boot/lib"

View File

@ -1,7 +1,13 @@
do do
tTasks,nPid,nTimeout,cPid = {},1,0,0 -- table of tasks, next process ID, event timeout, current PID local tTasks,nPid,nTimeout,cPid = {},1,0,0 -- table of tasks, next process ID, event timeout, current PID
function os.spawn(f,n) -- creates a process from function *f* with name *n* function os.spawn(f,n) -- creates a process from function *f* with name *n*
tTasks[nPid] = {["c"]=coroutine.create(f),["n"]=n,["p"]=nPid,e={}} tTasks[nPid] = {
c=coroutine.create(f), -- actual coroutine
n=n, -- process name
p=nPid, -- process PID
P=cPid, -- parent PID
e={} -- environment variables
}
if tTasks[cPid] then if tTasks[cPid] then
for k,v in pairs(tTasks[cPid].e) do for k,v in pairs(tTasks[cPid].e) do
tTasks[nPid].e[k] = tTasks[nPid].e[k] or v tTasks[nPid].e[k] = tTasks[nPid].e[k] or v
@ -13,7 +19,21 @@ end
function os.kill(pid) -- removes process *pid* from the task list function os.kill(pid) -- removes process *pid* from the task list
tTasks[pid] = nil tTasks[pid] = nil
end end
function sched() -- the actual scheduler function function os.pid()
return cPid
end
function os.tasks()
local rt = {}
for k,v in pairs(tTasks) do
rt[#rt+1] = k
end
return rt
end
function os.taskInfo(pid)
return {name=tTasks[pid].n,parent=tTasks[pid].P}
end
function os.sched() -- the actual scheduler function
os.sched = nil
while #tTasks > 0 do while #tTasks > 0 do
local tEv = {computer.pullSignal(nTimeout)} local tEv = {computer.pullSignal(nTimeout)}
for k,v in pairs(tTasks) do for k,v in pairs(tTasks) do

View File

@ -1,16 +0,0 @@
os.spawn(function()
print(_OSVERSION,tostring(computer.totalMemory()/1024).."K memory")
for k,v in pairs(fd) do
if v.t == "t" then
os.setenv("t") = k
print("Spawning Lua prompt for "..tostring(k))
os.setenv("PWD","/boot")
os.spawn(function() print(pcall(function() while true do
io.write(_VERSION.."> ")
tResult = {pcall(load(io.read()))}
for k,v in pairs(tResult) do
print(v)
end
end end)) end,"lua prompt")
end
end end,"init")

View File

@ -1,3 +1,5 @@
dprint=dprint or function() end
syslog = {} syslog = {}
syslog.emergency = 0 syslog.emergency = 0
syslog.alert = 1 syslog.alert = 1
@ -9,6 +11,7 @@ syslog.info = 6
syslog.debug = 7 syslog.debug = 7
setmetatable(syslog,{__call = function(_,msg, level, service) setmetatable(syslog,{__call = function(_,msg, level, service)
level, service = level or syslog.info, service or process.info().path level, service = level or syslog.info, service or os.taskInfo(os.pid()).name or "unknown"
dprint(string.format("syslog: [%s:%d/%d] %s",service,os.pid(),level,msg))
computer.pushSignal("syslog",msg, level, service) computer.pushSignal("syslog",msg, level, service)
end}) end})

5
module/term.lua Normal file
View File

@ -0,0 +1,5 @@
--#include "module/vt-task.lua"
do
local r,w = vtemu(component.list("gpu")(),component.list("screen")())
devfs.register("tty0", function() return r,w,function() end end)
end

View File

@ -1,13 +1,15 @@
do
--#include "module/vt100.lua"
function vtemu(gpua,scra) function vtemu(gpua,scra)
local gpu,scr = component.proxy(gpua),component.proxy(scra) local gpu = component.proxy(gpua)
gpu.bind(scra) gpu.bind(scra)
local write = vt100emu(gpu) local write = vt100emu(gpu)
local kba = {} local kba = {}
for k,v in ipairs(scr.getKeyboards()) do for k,v in ipairs(component.invoke(scra,"getKeyboards")) do
kba[v]=true kba[v]=true
end end
local buf = "" local buf = ""
os.spawn(function() os.spawn(function() dprint(pcall(function()
while true do while true do
local ty,ka,ch = coroutine.yield() local ty,ka,ch = coroutine.yield()
if ty == "key_down" and kba[ka] then if ty == "key_down" and kba[ka] then
@ -19,27 +21,17 @@ function vtemu(gpua,scra)
end end
elseif ch > 0 then elseif ch > 0 then
write(string.char(ch)) write(string.char(ch))
buf = buf .. string.char(ch) buf=buf..string.char(ch)
end end
end end
end end
end,"keyboard daemon for "..gpua:sub(1,8)..":"..scra:sub(1,8)) end)) end,string.format("ttyd[%s:%s]",gpua:sub(1,8),scra:sub(1,8)))
local function read(n) local function bread()
n = n or "\n" local n = buf:find("\n")
local rdata = "" if not n then return nil end
if type(n) == "number" then r, buf = buf:sub(1,n), buf:sub(n+1)
rdata = buf:sub(1,n) return r
return rdata
else
if n == "*a" then
rdata = buf
buf = ""
return rdata
end
local pr,po = buf:match("(.-)"..n.."(.*)")
buf = po or buf
return pr
end
end end
return read,write return bread, write, function() io.write("\27[2J\27[H") end
end
end end

View File

@ -58,9 +58,9 @@ function vt100emu(gpu) -- takes GPU component proxy *gpu* and returns a function
cx, cy = sx, sy cx, cy = sx, sy
mode = "n" mode = "n"
elseif cc == "H" then -- cursor home or to elseif cc == "H" then -- cursor home or to
local tx, ty = cs:match("(.);(.)") local tx, ty = cs:match("(.-);(.-)")
tx, ty = tx or "\1", ty or "\1" tx, ty = tx or "1", ty or "1"
cx, cy = string.byte(tx), string.byte(ty) cx, cy = tonumber(tx), tonumber(ty)
mode = "n" mode = "n"
elseif cc == "A" then -- cursor up elseif cc == "A" then -- cursor up
cy = cy - string.byte(n) cy = cy - string.byte(n)