@@ -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") |
@@ -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 | end |
@@ -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 |
@@ -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" |
@@ -0,0 +1,8 @@ | |||||
devfs.register("cons",function() | |||||
return function() | |||||
end, | |||||
function() | |||||
end, | |||||
function() | |||||
end | |||||
end) |
@@ -0,0 +1,3 @@ | |||||
devfs.register("null",function() | |||||
return function() end, function() end, function() end | |||||
end) |
@@ -0,0 +1,2 @@ | |||||
devfs.register("syslog",function() | |||||
return function() end, syslog, function() end end) |
@@ -1,24 +1,12 @@ | |||||
--#include "module/syslog.lua" | |||||
--#include "module/sched.lua" | --#include "module/sched.lua" | ||||
--#include "module/buffer.lua" | --#include "module/buffer.lua" | ||||
--#include "module/fs.lua" | --#include "module/fs.lua" | ||||
--#include "module/io.lua" | |||||
--#include "module/devfs.lua" | |||||
--#include "module/devfs/syslog.lua" | |||||
--#include "module/loadfile.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() |
@@ -8,7 +8,7 @@ function runfile(p,...) -- runs file *p* with arbitrary arguments in the current | |||||
return loadfile(p)(...) | return loadfile(p)(...) | ||||
end | end | ||||
function os.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" | ||||
@@ -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 |
@@ -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 | ||||
@@ -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}) |
@@ -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 |
@@ -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 |
@@ -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("(.);(.)") | |||||
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" | mode = "n" | ||||
elseif cc == "A" then -- cursor up | elseif cc == "A" then -- cursor up | ||||
cy = cy - string.byte(n) | cy = cy - string.byte(n) | ||||