diff --git a/exec/init.lua b/exec/init.lua new file mode 100644 index 0000000..1e7d13c --- /dev/null +++ b/exec/init.lua @@ -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") diff --git a/exec/ps.lua b/exec/ps.lua index 89d3ed5..c2bdb29 100644 --- a/exec/ps.lua +++ b/exec/ps.lua @@ -1,4 +1,5 @@ -print("PID# VTY# Name") -for k,v in pairs(tTasks) do - print(string.format("%4d %4d %s",k,v.e.t or 0,v.n)) +print("PID# Parent Name") +for k,v in pairs(os.tasks()) do + local t = os.taskInfo(v) + print(string.format("%4d %4d %s",k,v.parent,v.name)) end diff --git a/module/buffer.lua b/module/buffer.lua new file mode 100644 index 0000000..3d64b1b --- /dev/null +++ b/module/buffer.lua @@ -0,0 +1,171 @@ +local 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 diff --git a/module/devfs.lua b/module/devfs.lua new file mode 100644 index 0000000..e1f2a42 --- /dev/null +++ b/module/devfs.lua @@ -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" diff --git a/module/devfs/cons.lua b/module/devfs/cons.lua new file mode 100644 index 0000000..9c76eba --- /dev/null +++ b/module/devfs/cons.lua @@ -0,0 +1,8 @@ +devfs.register("cons",function() + return function() + end, + function() + end, + function() + end +end) diff --git a/module/devfs/null.lua b/module/devfs/null.lua new file mode 100644 index 0000000..6a2e996 --- /dev/null +++ b/module/devfs/null.lua @@ -0,0 +1,3 @@ +devfs.register("null",function() + return function() end, function() end, function() end +end) diff --git a/module/devfs/syslog.lua b/module/devfs/syslog.lua new file mode 100644 index 0000000..2fadd95 --- /dev/null +++ b/module/devfs/syslog.lua @@ -0,0 +1,2 @@ +devfs.register("syslog",function() + return function() end, syslog, function() end end) diff --git a/module/init.lua b/module/init.lua index 22999b0..2855e96 100644 --- a/module/init.lua +++ b/module/init.lua @@ -1,24 +1,12 @@ +--#include "module/syslog.lua" --#include "module/sched.lua" --#include "module/buffer.lua" --#include "module/fs.lua" +--#include "module/io.lua" +--#include "module/devfs.lua" +--#include "module/devfs/syslog.lua" --#include "module/loadfile.lua" -os.spawn(function() print(pcall(function() -print(_OSVERSION,tostring(math.floor(computer.totalMemory()/1024)).."K memory") -os.setenv("PWD","/boot") -local f = fs.open("/boot/init.txt","rb") -if f then - local fc = f:read("*a") - f:close() - for line in fc:gmatch("[^\n]+") do - print("Starting service "..line) - spawnfile("/boot/service/"..line,line) - end -end -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") +--#include "module/term.lua" +os.spawnfile("/boot/exec/init.lua","init") + +os.sched() diff --git a/module/loadfile.lua b/module/loadfile.lua index 3e4e662..6c29473 100644 --- a/module/loadfile.lua +++ b/module/loadfile.lua @@ -8,7 +8,7 @@ function runfile(p,...) -- runs file *p* with arbitrary arguments in the current return loadfile(p)(...) end 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 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" diff --git a/module/oldio.lua b/module/oldio.lua new file mode 100644 index 0000000..abd3417 --- /dev/null +++ b/module/oldio.lua @@ -0,0 +1,82 @@ +do +io = {} + +function io.type(fh) + if type(fh) ~= "table" then return nil end + if fh.state == "open" then + return "file" + elseif fh.state == "closed" then + return "closed file" + end + return nil +end + +function io.read(buf, n) + n = n or buf + buf = buf or io.input() + print("bread",type(buf),n) + if not buf.aread then return nil end + if not buf.abmode then + buffer.write(buf,buf.fh:read(buf.m - buf.b:len())) + end + local rv = buffer.read(buf,n) + buffer.write(buf,buf.fh:read(buf.m - buf.b:len())) + return rv +end +function io.write(buf, d) + d = d or buf + buf = buf or io.output() + print("bwrite",type(buf),d) + if not buf.awrite then return nil end + if buf.b:len() + d:len() > buf.m then + buf.fh:write(buffer.read(buf,buf.m)) + end + local rv = buffer.write(buf,d) + if not buf.abmode then + buf.fh:write(buffer.read(buf,buf.m)) + end + return rv +end + +function io.close(fh) + fh.fh.close() + fh.state = "closed" +end + +function io.flush() +end + +function io.open(fname,mode) + mode=mode or "r" + local buf = buffer.new() + buf.fh, er = fs.open(fname,mode) + if not buf.fh then + error(er) + end + buf.state = "open" + buf.aread = mode:match("r") + buf.awrite = mode:match("w") or mode:match("a") + setmetatable(buf,{__index=io}) + return buf +end + +function print(...) + for k,v in ipairs({...}) do + io.write(string.format("%s\n",tostring(v))) + end +end + +io.stdin = io.open("/dev/null") +io.stdout = io.open("/dev/null","w") + +function io.input(fname) + if not fname then return os.getenv("STDIN") or io.stdin end + os.setenv("STDIN",io.open(fname)) +end + +function io.output(fname) + if not fname then return os.getenv("STDOUT") or io.stdout end + os.setenv("STDOUT",io.open(fname,"w")) +end + +end diff --git a/module/sched.lua b/module/sched.lua index 70135ad..76611f9 100644 --- a/module/sched.lua +++ b/module/sched.lua @@ -1,7 +1,13 @@ 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* - 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 for k,v in pairs(tTasks[cPid].e) do 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 tTasks[pid] = nil 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 local tEv = {computer.pullSignal(nTimeout)} for k,v in pairs(tTasks) do diff --git a/module/syslog.lua b/module/syslog.lua index 5727caa..0c73715 100644 --- a/module/syslog.lua +++ b/module/syslog.lua @@ -1,3 +1,5 @@ +dprint=dprint or function() end + syslog = {} syslog.emergency = 0 syslog.alert = 1 @@ -9,6 +11,7 @@ syslog.info = 6 syslog.debug = 7 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) end}) diff --git a/module/term.lua b/module/term.lua new file mode 100644 index 0000000..7ab8749 --- /dev/null +++ b/module/term.lua @@ -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 diff --git a/module/vt-task.lua b/module/vt-task.lua new file mode 100644 index 0000000..f0032d5 --- /dev/null +++ b/module/vt-task.lua @@ -0,0 +1,37 @@ +do +--#include "module/vt100.lua" +function vtemu(gpua,scra) + local gpu = component.proxy(gpua) + gpu.bind(scra) + local write = vt100emu(gpu) + local kba = {} + for k,v in ipairs(component.invoke(scra,"getKeyboards")) do + kba[v]=true + end + local buf = "" + os.spawn(function() dprint(pcall(function() + while true do + local ty,ka,ch = coroutine.yield() + if ty == "key_down" and kba[ka] then + if ch == 13 then ch = 10 end + if ch == 8 then + if buf:len() > 0 then + write("\8 \8") + buf = buf:sub(1,-2) + end + elseif ch > 0 then + write(string.char(ch)) + buf=buf..string.char(ch) + end + end + end + end)) end,string.format("ttyd[%s:%s]",gpua:sub(1,8),scra:sub(1,8))) + local function bread() + local n = buf:find("\n") + if not n then return nil end + r, buf = buf:sub(1,n), buf:sub(n+1) + return r + end + return bread, write, function() io.write("\27[2J\27[H") end +end +end diff --git a/module/vt100.lua b/module/vt100.lua index 393429f..9285503 100644 --- a/module/vt100.lua +++ b/module/vt100.lua @@ -58,9 +58,9 @@ function vt100emu(gpu) -- takes GPU component proxy *gpu* and returns a function cx, cy = sx, sy mode = "n" elseif cc == "H" then -- cursor home or to - local tx, ty = cs:match("(.);(.)") - tx, ty = tx or "\1", ty or "\1" - cx, cy = string.byte(tx), string.byte(ty) + local tx, ty = cs:match("(.-);(.-)") + tx, ty = tx or "1", ty or "1" + cx, cy = tonumber(tx), tonumber(ty) mode = "n" elseif cc == "A" then -- cursor up cy = cy - string.byte(n)