1
1
mirror of https://git.shadowkat.net/izaya/OC-PsychOS2.git synced 2024-11-25 12:42:10 -05:00

Compare commits

...

21 Commits

Author SHA1 Message Date
XeonSquared
180a5a9a78 made event.lua work with the new restrictions on the scheduler and such 2019-11-09 16:09:23 +11:00
XeonSquared
37df78ffed corrected spelling in the serialization lib oops 2019-11-09 16:00:49 +11:00
XeonSquared
80eea7142d made init less syslog-spammy 2019-11-09 16:00:46 +11:00
XeonSquared
256e05233a updated fsmanager and included it in the default init 2019-11-09 15:56:43 +11:00
XeonSquared
a219b5603e added the ability to unmount filesystems, removed fs mounting stuff on its own 2019-11-09 15:56:25 +11:00
XeonSquared
a97e3c93ee made init not kill itself on startup 2019-11-09 14:59:53 +11:00
XeonSquared
ec699518b5 made df wrap numbers better 2019-11-09 14:59:34 +11:00
XeonSquared
faca451c57 added some filesystem-related utils 2019-11-09 14:54:01 +11:00
XeonSquared
aed9278433 made getty actually start relevant services and re-start on failure 2019-11-09 14:23:47 +11:00
XeonSquared
6ad6880a6d rewrote init.lua to be a real init daemon 2019-11-09 14:23:18 +11:00
XeonSquared
1bd18f45b3 made os.taskInfo() default to the current process 2019-11-09 13:52:39 +11:00
XeonSquared
d23a25613d made ps display the pid correctly 2019-11-09 13:48:50 +11:00
XeonSquared
ac98d09b93 made loadfile less chatty 2019-11-09 13:46:51 +11:00
XeonSquared
a335ff5c87 made ed not choke when you append an empty file 2019-11-09 13:46:31 +11:00
XeonSquared
e9aac95dd7 added a mkdir alias for fs.makeDirectory in the shell 2019-11-09 13:17:21 +11:00
XeonSquared
150541d91b made :read()ing from a terminal not leave the trailing \n 2019-11-09 13:16:51 +11:00
XeonSquared
6b5677b870 made dprint go through syslog in all cases 2019-11-09 13:16:17 +11:00
XeonSquared
5f8e4efe33 made the devfs module provide saner respones to some queries 2019-11-09 13:15:50 +11:00
XeonSquared
98e3581e6e made os.taskInfo() not choke on being outside a process 2019-11-09 13:14:58 +11:00
XeonSquared
23680afd75 document new functions, remove dprints 2019-11-09 13:10:15 +11:00
XeonSquared
69eae00ec1 added fs.mounts(), fs.address(path) and fs.type(path) to provide more information about mounted filesystems 2019-11-08 21:01:01 +11:00
18 changed files with 197 additions and 187 deletions

View File

@ -1,9 +1,11 @@
#!/bin/sh #!/bin/sh
rm -r target/* rm -r target/*
mkdir target mkdir target &>/dev/null
mkdir target/cfg
lua luapreproc.lua module/init.lua target/init.lua lua luapreproc.lua module/init.lua target/init.lua
echo _OSVERSION=\"PsychOS 2.0a1-$(git rev-parse --short HEAD)\" > target/version.lua echo _OSVERSION=\"PsychOS 2.0a1-$(git rev-parse --short HEAD)\" > target/version.lua
cat target/version.lua target/init.lua > target/tinit.lua cat target/version.lua target/init.lua > target/tinit.lua
mv target/tinit.lua target/init.lua mv target/tinit.lua target/init.lua
cp -r exec/ service/ lib/ target/ cp -r exec/ service/ lib/ target/
cp default-init.txt target/cfg/
lua finddesc.lua $(find module/ -type f) $(find lib/ -type f) > apidoc.md lua finddesc.lua $(find module/ -type f) $(find lib/ -type f) > apidoc.md

View File

@ -1,3 +1,2 @@
minitel.lua getty.lua
tape-iofs.lua fsmanager.lua
fsmanager.service

23
exec/df.lua Normal file
View File

@ -0,0 +1,23 @@
local mt = fs.mounts()
local ml = 0
for k,v in pairs(mt) do
if v:len() > ml then
ml = v:len()
end
end
local scale = {"K","M","G","T","P"}
local function wrapUnits(n)
local count = 0
while n > 1024 do
count = count + 1
if not scale[count] then return "inf" end
n = n / 1024
end
return tostring(math.floor(n))..(scale[count] or "")
end
local fstr = "%-"..tostring(ml).."s %5s %5s"
print("fs"..(" "):rep(ml-2).." size used")
for k,v in pairs(mt) do
local st, su = fs.spaceTotal(v), fs.spaceUsed(v)
print(string.format(fstr,v,wrapUnits(st),wrapUnits(su)))
end

View File

@ -63,6 +63,7 @@ end
function ct.append(np) function ct.append(np)
ct.sp(np) ct.sp(np)
p=p+1 p=p+1
if #ft < 1 then p = 1 end
ct.insert() ct.insert()
end end
function ct.delete(np,n) function ct.delete(np,n)

View File

@ -1,16 +1,25 @@
xpcall(function() if os.taskInfo(1) and os.pid() ~= 1 then
os.setenv("PWD","/boot") return false, "init already started"
os.spawnfile("/boot/service/getty.lua")
coroutine.yield()
for k,v in pairs(fs.list("/dev/")) do
if v:sub(1,3) == "tty" then
dprint(tostring(io.input("/dev/"..v)))
dprint(tostring(io.output("/dev/"..v)))
print(_OSVERSION.." - "..tostring(math.floor(computer.totalMemory()/1024)).."K RAM")
os.spawnfile("/boot/exec/shell.lua")
end
end end
os.setenv("PWD","/boot")
io.input("/dev/null")
io.output("/dev/syslog")
local pids = {}
local function loadlist()
local f = io.open("/boot/cfg/init.txt","rb")
if not f then return false end
for line in f:read("*a"):gmatch("[^\r\n]+") do
pids[line] = -1
end
f:close()
end
loadlist()
while true do while true do
for k,v in pairs(pids) do
if not os.taskInfo(v) then
syslog("Starting service "..k)
pids[k] = os.spawnfile("/boot/service/"..k)
end
end
coroutine.yield() coroutine.yield()
end end
end,function(e) dprint(e) end,"init")

24
exec/mount.lua Normal file
View File

@ -0,0 +1,24 @@
local tA = {...}
if #tA < 1 then
local mt = fs.mounts()
for k,v in pairs(mt) do
print(tostring(fs.address(v)).." on "..tostring(v).." type "..fs.type(v))
end
else
local addr,path = tA[1],tA[2]
local fscomp = component.list("filesystem")
if not fscomp[addr] then
for k,v in pairs(fscomp) do
if k:find(addr) then
print(tostring(addr).." not found, assuming you meant "..k)
addr = k
break
end
end
end
local proxy = component.proxy(addr)
if not proxy then
return false, "no such filesystem component"
end
print(fs.mount(path,proxy))
end

View File

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

View File

@ -4,6 +4,7 @@ function shenv.quit()
os.setenv("run",nil) os.setenv("run",nil)
end end
shenv.cd = os.chdir shenv.cd = os.chdir
shenv.mkdir = fs.makeDirectory
setmetatable(shenv,{__index=function(_,k) setmetatable(shenv,{__index=function(_,k)
if _G[k] then if _G[k] then
return _G[k] return _G[k]

View File

@ -23,16 +23,13 @@ function event.pull(t,...) -- return an event, optionally with timeout *t* and f
end end
function event.listen(e,f) -- run function *f* for every occurance of event *e* function event.listen(e,f) -- run function *f* for every occurance of event *e*
local op = os.getenv("parent")
os.setenv("parent",cPid)
os.spawn(function() while true do os.spawn(function() while true do
local tEv = {coroutine.yield()} local tEv = {coroutine.yield()}
if tEv[1] == e then if tEv[1] == e then
f(table.unpack(tEv)) f(table.unpack(tEv))
end end
if not tTasks[os.getenv("parent")] or (tEv[1] == "unlisten" and tEv[2] == e and tEv[3] == tostring(f)) then break end if not os.taskInfo(os.taskInfo().parent) or (tEv[1] == "unlisten" and tEv[2] == e and tEv[3] == tostring(f)) then break end
end end,string.format("[%d] %s listener",cPid,e)) end end,string.format("[%d] %s listener",os.pid(),e))
os.setenv("parent",op)
end end
function event.ignore(e,f) -- stop function *f* running for every occurance of event *e* function event.ignore(e,f) -- stop function *f* running for every occurance of event *e*

View File

@ -1,145 +1,49 @@
-- serialization lib borrowed from OpenOS local serial = {}
local serialization = {} local local_pairs=function(tbl)
local mt=getmetatable(tbl)
-- delay loaded tables fail to deserialize cross [C] boundaries (such as when having to read files that cause yields) return (mt and mt.__pairs or pairs)(tbl)
local local_pairs = function(tbl)
local mt = getmetatable(tbl)
return (mt and mt.__pairs or pairs)(tbl)
end end
function serial.serialize(value)
-- Important: pretty formatting will allow presenting non-serializable values local kw={["and"]=true,["break"]=true,["do"]=true,["else"]=true,["elseif"]=true,["end"]=true,["false"]=true,["for"]=true,["function"]=true,["goto"]=true,["if"]=true,["in"]=true,["local"]=true,["nil"]=true,["not"]=true,["or"]=true,["repeat"]=true,["return"]=true,["then"]=true,["true"]=true,["until"]=true,["while"]=true}
-- but may generate output that cannot be unserialized back. local id="^[%a_][%w_]*$"
function serialization.serialize(value, pretty) -- serialize *value* into a string, optionally in a nicely formatted manner when *pretty* is set local ts={}
local kw = {["and"]=true, ["break"]=true, ["do"]=true, ["else"]=true, local function s(v,l)
["elseif"]=true, ["end"]=true, ["false"]=true, ["for"]=true, local t=type(v)
["function"]=true, ["goto"]=true, ["if"]=true, ["in"]=true, if t=="nil" then return "nil"
["local"]=true, ["nil"]=true, ["not"]=true, ["or"]=true, elseif t=="boolean" then return v and "true" or "false"
["repeat"]=true, ["return"]=true, ["then"]=true, ["true"]=true, elseif t=="number" then
["until"]=true, ["while"]=true} if v~=v then return "0/0"
local id = "^[%a_][%w_]*$" elseif v==math.huge then return "math.huge"
local ts = {} elseif v==-math.huge then return "-math.huge"
local result_pack = {} else return tostring(v) end
local function recurse(current_value, depth) elseif t=="string" then return string.format("%q",v):gsub("\\\n","\\n")
local t = type(current_value) elseif t=="table" then
if t == "number" then if ts[v] then error("tcyc") end
if current_value ~= current_value then ts[v]=true
table.insert(result_pack, "0/0") local i,r=1, nil
elseif current_value == math.huge then local f=table.pack(local_pairs(v))
table.insert(result_pack, "math.huge") for k,v in table.unpack(f) do
elseif current_value == -math.huge then if r then r=r..","..(("\n"..string.rep(" ",l)) or "")
table.insert(result_pack, "-math.huge") else r="{" end
else local tk=type(k)
table.insert(result_pack, tostring(current_value)) if tk=="number" and k==i then
end i=i+1
elseif t == "string" then r=r..s(v,l+1)
table.insert(result_pack, (string.format("%q", current_value):gsub("\\\n","\\n")))
elseif
t == "nil" or
t == "boolean" or
pretty and (t ~= "table" or (getmetatable(current_value) or {}).__tostring) then
table.insert(result_pack, tostring(current_value))
elseif t == "table" then
if ts[current_value] then
if pretty then
table.insert(result_pack, "recursion")
return
else
error("tables with cycles are not supported")
end
end
ts[current_value] = true
local f
if pretty then
local ks, sks, oks = {}, {}, {}
for k in local_pairs(current_value) do
if type(k) == "number" then
table.insert(ks, k)
elseif type(k) == "string" then
table.insert(sks, k)
else
table.insert(oks, k)
end
end
table.sort(ks)
table.sort(sks)
for _, k in ipairs(sks) do
table.insert(ks, k)
end
for _, k in ipairs(oks) do
table.insert(ks, k)
end
local n = 0
f = table.pack(function()
n = n + 1
local k = ks[n]
if k ~= nil then
return k, current_value[k]
else
return nil
end
end)
else
f = table.pack(local_pairs(current_value))
end
local i = 1
local first = true
table.insert(result_pack, "{")
for k, v in table.unpack(f) do
if not first then
table.insert(result_pack, ",")
if pretty then
table.insert(result_pack, "\n" .. string.rep(" ", depth))
end
end
first = nil
local tk = type(k)
if tk == "number" and k == i then
i = i + 1
recurse(v, depth + 1)
else
if tk == "string" and not kw[k] and string.match(k, id) then
table.insert(result_pack, k)
else
table.insert(result_pack, "[")
recurse(k, depth + 1)
table.insert(result_pack, "]")
end
table.insert(result_pack, "=")
recurse(v, depth + 1)
end
end
ts[current_value] = nil -- allow writing same table more than once
table.insert(result_pack, "}")
else else
error("unsupported type: " .. t) if tk == "string" and not kw[k] and string.match(k,id) then r=r..k
end else r=r.."["..s(k,l+1).."]" end
end r=r.."="..s(v,l+1) end end
recurse(value, 1) ts[v]=nil
local result = table.concat(result_pack) return (r or "{").."}"
if pretty then else error("ut "..t) end end
local limit = type(pretty) == "number" and pretty or 10 return s(value, 1)
local truncate = 0
while limit > 0 and truncate do
truncate = string.find(result, "\n", truncate + 1, true)
limit = limit - 1
end
if truncate then
return result:sub(1, truncate) .. "..."
end
end
return result
end end
function serial.unserialize(data)
function serialization.unserialize(data) -- returns the data contained in serialized string *data* checkArg(1, data, "string")
local result, reason = load("return " .. data, "=data", nil, {math={huge=math.huge}}) local result, reason = load("return " .. data, "=data", _, {math={huge=math.huge}})
if not result then if not result then return nil, reason end
return nil, reason local ok, output = pcall(result)
end if not ok then return nil, output end
local ok, output = pcall(result) return output
if not ok then
return nil, output
end
return output
end end
return serial
return serialization

View File

@ -7,10 +7,13 @@ devfs.component = {}
local function rfalse() local function rfalse()
return false return false
end end
local function rzero()
return 0
end
function devfs.component.getLabel() function devfs.component.getLabel()
return "devfs" return "devfs"
end 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 devfs.component.spaceUsed, devfs.component.spaceTotal, devfs.component.isReadOnly, devfs.component.isDirectory,devfs.component.size, devfs.component.setLabel = rzero, rzero, rfalse, rfalse, rzero, rfalse
function devfs.component.exists(fname) function devfs.component.exists(fname)
return devfs.files[fname] ~= nil return devfs.files[fname] ~= nil

View File

@ -15,11 +15,8 @@ function fs.resolve(path) -- resolves *path* to a specific filesystem mount and
if path:sub(1,1) ~= "/" then path=(os.getenv("PWD") or "").."/"..path end if path:sub(1,1) ~= "/" then path=(os.getenv("PWD") or "").."/"..path end
local segments, rpath, rfs= fs.segments(path) local segments, rpath, rfs= fs.segments(path)
local rc = #segments local rc = #segments
dprint(rc)
for i = #segments, 1, -1 do for i = #segments, 1, -1 do
dprint("testing "..table.concat(segments, "/", 1, i),tostring(fsmounts[table.concat(segments, "/", 1, i)]))
if fsmounts[table.concat(segments, "/", 1, i)] ~= nil then if fsmounts[table.concat(segments, "/", 1, i)] ~= nil then
dprint("ret",table.concat(segments, "/", 1, i), table.concat(segments, "/", i+1))
return table.concat(segments, "/", 1, i), table.concat(segments, "/", i+1) return table.concat(segments, "/", 1, i), table.concat(segments, "/", i+1)
end end
end end
@ -27,7 +24,7 @@ function fs.resolve(path) -- resolves *path* to a specific filesystem mount and
end end
-- generate some simple functions -- generate some simple functions
for k,v in pairs({"makeDirectory","exists","isDirectory","list","lastModified","remove","size","spaceUsed","isReadOnly","getLabel"}) do for k,v in pairs({"makeDirectory","exists","isDirectory","list","lastModified","remove","size","spaceUsed","spaceTotal","isReadOnly","getLabel"}) do
fs[v] = function(path) fs[v] = function(path)
local fsi,path = fs.resolve(path) local fsi,path = fs.resolve(path)
return fsmounts[fsi][v](path) return fsmounts[fsi][v](path)
@ -99,13 +96,34 @@ function fs.rename(from,to) -- moves file *from* to *to*
return true return true
end end
function fs.mount(path,proxy) function fs.mount(path,proxy) -- mounts the filesystem *proxy* to the mount point *path* if it is a directory. BYO proxy.
if fs.isDirectory(path) then if fs.isDirectory(path) and not fsmounts[table.concat(fs.segments(path),"/")] then
fsmounts[table.concat(fs.segments(path),"/")] = proxy fsmounts[table.concat(fs.segments(path),"/")] = proxy
return true return true
end end
return false, "path is not a directory" return false, "path is not a directory"
end end
function fs.umount(path)
local fsi,_ = fs.resolve(path)
fsmounts[fsi] = nil
end
function fs.mounts() -- returns a table containing the mount points of all mounted filesystems
local rt = {}
for k,v in pairs(fsmounts) do
rt[#rt+1] = k,v.address or "unknown"
end
return rt
end
function fs.address(path) -- returns the address of the filesystem at a given path, if applicable
local fsi,_ = fs.resolve(path)
return fsmounts[fsi].address
end
function fs.type(path) -- returns the component type of the filesystem at a given path, if applicable
local fsi,_ = fs.resolve(path)
return fsmounts[fsi].type
end
fsmounts["/"] = component.proxy(computer.tmpAddress()) fsmounts["/"] = component.proxy(computer.tmpAddress())
fs.makeDirectory("temp") fs.makeDirectory("temp")
@ -113,9 +131,5 @@ if computer.getBootAddress then
fs.makeDirectory("boot") fs.makeDirectory("boot")
fs.mount("boot",component.proxy(computer.getBootAddress())) fs.mount("boot",component.proxy(computer.getBootAddress()))
end end
for addr, _ in component.list("filesystem") do
fs.makeDirectory(addr:sub(1,3))
fs.mount(addr:sub(1,3),component.proxy(addr))
end
end end

View File

@ -8,7 +8,6 @@ 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*
dprint(p,n)
return os.spawn(function() xpcall(loadfile(p),function(e) dprint(e.."\n"..debug.traceback()) end) end,n or p) return os.spawn(function() xpcall(loadfile(p),function(e) dprint(e.."\n"..debug.traceback()) end) end,n or p)
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

View File

@ -30,6 +30,8 @@ function os.tasks()
return rt return rt
end end
function os.taskInfo(pid) function os.taskInfo(pid)
pid = pid or os.pid()
if not tTasks[pid] then return false end
return {name=tTasks[pid].n,parent=tTasks[pid].P} return {name=tTasks[pid].n,parent=tTasks[pid].P}
end end
function os.sched() -- the actual scheduler function function os.sched() -- the actual scheduler function

View File

@ -1,5 +1,4 @@
dprint=dprint or function() end do
syslog = {} syslog = {}
syslog.emergency = 0 syslog.emergency = 0
syslog.alert = 1 syslog.alert = 1
@ -10,8 +9,15 @@ syslog.notice = 5
syslog.info = 6 syslog.info = 6
syslog.debug = 7 syslog.debug = 7
local rdprint=dprint or function() end
setmetatable(syslog,{__call = function(_,msg, level, service) setmetatable(syslog,{__call = function(_,msg, level, service)
level, service = level or syslog.info, service or os.taskInfo(os.pid()).name or "unknown" level, service = level or syslog.info, service or (os.taskInfo(os.pid()) or {}).name or "unknown"
dprint(string.format("syslog: [%s:%d/%d] %s",service,os.pid(),level,msg)) rdprint(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})
function dprint(...)
for k,v in pairs({...}) do
syslog(v,syslog.debug)
end
end
end

View File

@ -31,8 +31,7 @@ function vtemu(gpua,scra) -- creates a process to handle the GPU and screen addr
coroutine.yield() coroutine.yield()
end end
local n = buf:find("\n") local n = buf:find("\n")
r, buf = buf:sub(1,n), buf:sub(n+1) r, buf = buf:sub(1,n-1), buf:sub(n+1)
dprint("bread",r)
return r return r
end end
return bread, write, function() io.write("\27[2J\27[H") end return bread, write, function() io.write("\27[2J\27[H") end

View File

@ -1,11 +1,21 @@
local function mount(addr)
dest = component.invoke(addr,"getLabel") or "mnt/"..addr:sub(1,3)
dest = "/"..dest
syslog("Mounting "..addr.." to "..dest)
fs.makeDirectory(dest)
local w,r = fs.mount(dest,component.proxy(addr))
if not w then
syslog("Failed to mount: "..r)
end
end
for addr, _ in component.list("filesystem") do
mount(addr)
end
while true do while true do
local tE = {coroutine.yield()} local tE = {coroutine.yield()}
if tE[1] == "component_added" and tE[3] == "filesystem" then if tE[1] == "component_added" and tE[3] == "filesystem" then
local w, doesExist = pcall(fs.exists,"/"..tE[2]:sub(1,3)) mount(tE[2])
if not w or not doesExist then
fs.mounts[tE[2]:sub(1,3)] = component.proxy(tE[2])
end
elseif tE[1] == "component_removed" and tE[3] == "filesystem" then elseif tE[1] == "component_removed" and tE[3] == "filesystem" then
fs.mounts[tE[2]:sub(1,3)] = nil fs.umount("/mnt/"..tE[2]:sub(1,3))
end end
end end

View File

@ -1,4 +1,4 @@
local gpus,screens,ttyn = {}, {}, 0 local gpus,screens,ttyn,pids = {}, {}, 0, {}
local function scan() local function scan()
local w,di = pcall(computer.getDeviceInfo) local w,di = pcall(computer.getDeviceInfo)
if w then if w then
@ -29,6 +29,14 @@ local function nextScreen(n)
end end
return rt[n] or rt[8000] or rt[2000] or rt[600] return rt[n] or rt[8000] or rt[2000] or rt[600]
end end
local function spawnShell(fin,fout)
io.input(fin)
io.output(fout)
print(_OSVERSION.." - "..tostring(math.floor(computer.totalMemory()/1024)).."K RAM")
return os.spawnfile("/boot/exec/shell.lua")
end
local function allocate() local function allocate()
for k,v in pairs(gpus) do for k,v in pairs(gpus) do
dprint(k) dprint(k)
@ -38,13 +46,22 @@ local function allocate()
devfs.register("tty"..tostring(ttyn), function() return r,w,function() end end) devfs.register("tty"..tostring(ttyn), function() return r,w,function() end end)
gpus[k][1] = true gpus[k][1] = true
screens[sA][1] = true screens[sA][1] = true
pids["tty"..tostring(ttyn)] = {-1}
ttyn = ttyn + 1 ttyn = ttyn + 1
end end
end end
end end
scan() scan()
allocate() allocate()
dprint("screens ready") dprint("screens ready")
while true do while true do
coroutine.yield() coroutine.yield()
for k,v in pairs(pids) do
if not os.taskInfo(v[1]) then
dprint("Spawning new shell for "..k)
pids[k][1] = spawnShell(v[2] or "/dev/"..k, v[3] or "/dev/"..k)
pids[k][2], pids[k][3] = pids[k][2] or io.input(), pids[k][3] or io.output()
end
end
end end