memory, sandbox, devfs, signals, log, rc, terminal, oppm

A big update final push before 1.6 release. Here is the detailed rundown of the changes

/lib/term.lua
1. delay calling gpu.getViewport() when creating a terminal window. this fixes scenarios where the gpu could be a proxy object that doesn't have a viewport defined yet.
2. big blink cleanup to minimize the potential number of gpu calls during blink, and simplify the code

/lib/sh.lua, /lib/process.lua
1. moving shell sandboxing code to process library. This actually simplifies creating the sandbox for processes and handling process crash (process lib and sh lib shared common crash code)

/bin/rc.lua
1. found a bug in restart, fixed

/lib/pipes.lua
1. required update to be compatible with internal(private) methods to the sh library

/lib/package.lua
1. just aesthetic cleanup of error reporting

/bin/mktmp.lua
1. use existing os.tmpname() helper method

/init.lua and (added) /lib/tools/boot.lua
1. moving all the code I can to tools file to allow the memory to unload after boot completes.

/bin/ls.lua
1. fixing symbolic links to directory display (had extra /)

/lib/event.lua
1. refactor listeners and timers into common registers (fully backwards compatible). Provides a mechanism for drivers to know about ALL events without stealing them from the main process. Will document it later when the api is hardened (new api, event.register)
2. memory savings due to refactor
3. protecting computer.pullSignal! this is critical, user scripts that were previously pulling diirection from the computer api were able to put the kernel in a bad state. computer.pullSignal is still available, but it calls the event api to correctly dispatch events as drivers expect.

devfs
1. eeprom and eeprom-data are now dynamically available. This is significant, now when a user removes the eeprom, it will not be listed in /dev
2. devfs upgrade, support built for future dynamic folders (such as /dev/filesystems/)

autorun
1. code cleanup

oppm
1. fixing cwd expected by oppm.lua during initial install (if using .install via `install oppm`)
This commit is contained in:
payonel 2016-08-26 23:33:20 -07:00
parent 9dcc6070e0
commit 917befcd0e
16 changed files with 703 additions and 645 deletions

View File

@ -1,12 +1,11 @@
local fs = require("filesystem") local fs = require("filesystem")
local uuid = require("uuid")
local shell = require("shell") local shell = require("shell")
local sh = require("sh") local sh = require("sh")
local touch = loadfile(shell.resolve("touch", "lua")) local touch = loadfile(shell.resolve("touch", "lua"))
local mkdir = loadfile(shell.resolve("mkdir", "lua")) local mkdir = loadfile(shell.resolve("mkdir", "lua"))
if not uuid or not touch then if not touch then
local errorMessage = "missing tools for mktmp" local errorMessage = "missing tools for mktmp"
io.stderr:write(errorMessage .. '\n') io.stderr:write(errorMessage .. '\n')
return false, errorMessage return false, errorMessage
@ -57,24 +56,14 @@ if not fs.exists(prefix) then
return 1 return 1
end end
while true do local tmp = os.tmpname()
local tmp = prefix .. uuid.next() local ok, reason = (directory and mkdir or touch)(tmp)
if not fs.exists(tmp) then
local ok, reason if sh.internal.command_passed(ok) then
if directory then
ok, reason = mkdir(tmp)
else
ok, reason = touch(tmp)
end
if sh.internal.command_passed(ok) then
if verbose then if verbose then
print(tmp) print(tmp)
end end
return tmp return tmp
else
return ok, reason
end
end
end end
return ok, reason

View File

@ -65,9 +65,10 @@ local function rawRunCommand(conf, name, cmd, args, ...)
return true return true
end end
elseif cmd == "restart" and type(result["stop"]) == "function" and type(result["start"]) == "function" then elseif cmd == "restart" and type(result["stop"]) == "function" and type(result["start"]) == "function" then
result, what = xpcall(result["stop"], debug.traceback, ...) local daemon = result
result, what = xpcall(daemon["stop"], debug.traceback, ...)
if result then if result then
result, what = xpcall(result["start"], debug.traceback, ...) result, what = xpcall(daemon["start"], debug.traceback, ...)
if result then if result then
return true return true
end end

View File

@ -13,7 +13,7 @@ local function onInit()
local path = fs.concat(os.getenv("TMPDIR") or "/tmp", "event.log") local path = fs.concat(os.getenv("TMPDIR") or "/tmp", "event.log")
local log = io.open(path, "a") local log = io.open(path, "a")
if log then if log then
log:write(reason .. "\n") log:write(tostring(result) .. ":" .. tostring(reason) .. "\n")
log:close() log:close()
end end
end end
@ -34,15 +34,11 @@ local function onComponentAdded(_, address, componentType)
name = fs.concat("/mnt", name) name = fs.concat("/mnt", name)
fs.mount(proxy, name) fs.mount(proxy, name)
if fs.isAutorunEnabled() then if fs.isAutorunEnabled() then
local function run()
local file = shell.resolve(fs.concat(name, "autorun"), "lua") or local file = shell.resolve(fs.concat(name, "autorun"), "lua") or
shell.resolve(fs.concat(name, ".autorun"), "lua") shell.resolve(fs.concat(name, ".autorun"), "lua")
if file then if file then
local result, reason = shell.execute(file, _ENV, proxy) local run = function()
if not result then assert(shell.execute(file, _ENV, proxy))
error(reason, 0)
end
end
end end
if isInitialized then if isInitialized then
run() run()
@ -52,6 +48,7 @@ local function onComponentAdded(_, address, componentType)
end end
end end
end end
end
end end
local function onComponentRemoved(_, address, componentType) local function onComponentRemoved(_, address, componentType)

View File

@ -1,167 +1,21 @@
do do
_G._OSVERSION = "OpenOS 1.6" local loadfile = load([[return function(file)
local handle, reason = invoke(addr, "open", file)
local component = component
local computer = computer
local unicode = unicode
-- Runlevel information.
local runlevel, shutdown = "S", computer.shutdown
computer.runlevel = function() return runlevel end
computer.shutdown = function(reboot)
runlevel = reboot and 6 or 0
if os.sleep then
computer.pushSignal("shutdown")
os.sleep(0.1) -- Allow shutdown processing.
end
shutdown(reboot)
end
-- Low level dofile implementation to read filesystem libraries.
local rom = {}
function rom.invoke(method, ...)
return component.invoke(computer.getBootAddress(), method, ...)
end
function rom.open(file) return rom.invoke("open", file) end
function rom.read(handle) return rom.invoke("read", handle, math.huge) end
function rom.close(handle) return rom.invoke("close", handle) end
function rom.inits() return ipairs(rom.invoke("list", "boot")) end
function rom.isDirectory(path) return rom.invoke("isDirectory", path) end
local screen = component.list('screen', true)()
for address in component.list('screen', true) do
if #component.invoke(address, 'getKeyboards') > 0 then
screen = address
break
end
end
_G.boot_screen = screen
-- Report boot progress if possible.
local gpu = component.list("gpu", true)()
local w, h
if gpu and screen then
component.invoke(gpu, "bind", screen)
w, h = component.invoke(gpu, "maxResolution")
component.invoke(gpu, "setResolution", w, h)
component.invoke(gpu, "setBackground", 0x000000)
component.invoke(gpu, "setForeground", 0xFFFFFF)
component.invoke(gpu, "fill", 1, 1, w, h, " ")
end
local y = 1
local function status(msg)
if gpu and screen then
component.invoke(gpu, "set", 1, y, msg)
if y == h then
component.invoke(gpu, "copy", 1, 2, w, h - 1, 0, -1)
component.invoke(gpu, "fill", 1, h, w, 1, " ")
else
y = y + 1
end
end
end
status("Booting " .. _OSVERSION .. "...")
-- Custom low-level loadfile/dofile implementation reading from our ROM.
local function loadfile(file)
status("> " .. file)
local handle, reason = rom.open(file)
if not handle then if not handle then
error(reason) error(reason)
end end
local buffer = "" local buffer = ""
repeat repeat
local data, reason = rom.read(handle) local data, reason = invoke(addr, "read", handle, math.huge)
if not data and reason then if not data and reason then
error(reason) error(reason)
end end
buffer = buffer .. (data or "") buffer = buffer .. (data or "")
until not data until not data
rom.close(handle) invoke(addr, "close", handle)
return load(buffer, "=" .. file) return load(buffer, "=" .. file, "bt", _G)
end end]], "=loadfile", "bt", {load=load,math=math,addr=computer.getBootAddress(), invoke=component.invoke})()
loadfile("/lib/tools/boot.lua")(loadfile)
local function dofile(file)
local program, reason = loadfile(file)
if program then
local result = table.pack(pcall(program))
if result[1] then
return table.unpack(result, 2, result.n)
else
error(result[2])
end
else
error(reason)
end
end
status("Initializing package management...")
-- Load file system related libraries we need to load other stuff moree
-- comfortably. This is basically wrapper stuff for the file streams
-- provided by the filesystem components.
local package = dofile("/lib/package.lua")
do
-- Unclutter global namespace now that we have the package module.
_G.component = nil
_G.computer = nil
_G.process = nil
_G.unicode = nil
-- Initialize the package module with some of our own APIs.
package.loaded.component = component
package.loaded.computer = computer
package.loaded.unicode = unicode
package.preload["buffer"] = loadfile("/lib/buffer.lua")
package.preload["filesystem"] = loadfile("/lib/filesystem.lua")
-- Inject the package and io modules into the global namespace, as in Lua.
_G.package = package
_G.io = loadfile("/lib/io.lua")()
--mark modules for delay loaded api
package.delayed["text"] = true
package.delayed["sh"] = true
package.delayed["transforms"] = true
package.delayed["term"] = true
end
status("Initializing file system...")
-- Mount the ROM and temporary file systems to allow working on the file
-- system module from this point on.
require("filesystem").mount(computer.getBootAddress(), "/")
package.preload={}
status("Running boot scripts...")
-- Run library startup scripts. These mostly initialize event handlers.
local scripts = {}
for _, file in rom.inits() do
local path = "boot/" .. file
if not rom.isDirectory(path) then
table.insert(scripts, path)
end
end
table.sort(scripts)
for i = 1, #scripts do
dofile(scripts[i])
end
status("Initializing components...")
for c, t in component.list() do
computer.pushSignal("component_added", c, t)
end
os.sleep(0.5) -- Allow signal processing by libraries.
status("Initializing system...")
computer.pushSignal("init") -- so libs know components are initialized.
require("event").pull(1, "init") -- Allow init processing.
runlevel = 1
end end
while true do while true do

View File

@ -1,5 +1,8 @@
local fs = require("filesystem") local fs = require("filesystem")
local comp = require("component") local comp = require("component")
local text = require("text")
local sys = {} -- base class
local function new_node(parent, name, is_dir, proxy) local function new_node(parent, name, is_dir, proxy)
local node = {parent=parent, name=name, is_dir=is_dir, proxy=proxy} local node = {parent=parent, name=name, is_dir=is_dir, proxy=proxy}
@ -9,24 +12,30 @@ local function new_node(parent, name, is_dir, proxy)
return node return node
end end
local function new_devfs_dir(name) -- node may support isAvailable, and may choose to not be available
local sys = {} local function isAvailable(node)
sys.mtab = new_node(nil, name or "/", true) return node and not (node.proxy and node.proxy.isAvailable and not node.proxy.isAvailable())
end
-- returns: dir, point or path -- returns: dir, point or path
-- node (table): the handler responsible for the path -- node (table): the handler responsible for the path
-- this is essentially the device filesystem that is registered for the given path -- this is essentially the device filesystem that is registered for the given path
-- point (string): the point name (like a file name) -- point (string): the point name (like a file name)
function sys.findNode(path, create) function sys:findNode(path, create)
checkArg(1, path, "string") checkArg(1, path, "string")
local segments = fs.segments(path) local segments = fs.segments(path)
local node = sys.mtab local node = self.mtab
while #segments > 0 do while #segments > 0 do
local name = table.remove(segments, 1) local name = table.remove(segments, 1)
local prev_path = path local prev_path = path
path = table.concat(segments, "/") path = table.concat(segments, "/")
if not node.children[name] then local next_node = node.children[name]
if not isAvailable(next_node) then
next_node = nil
end
if not next_node then
if not create then if not create then
path = prev_path path = prev_path
break break
@ -46,10 +55,10 @@ local function new_devfs_dir(name)
if path == "" or node.is_dir and node.proxy then if path == "" or node.is_dir and node.proxy then
return node, path return node, path
end end
end end
function sys.invoke(method, path, ...) function sys:invoke(method, path, ...)
local node, rest = sys.findNode(path) local node, rest = self.findNode(path)
if not node or -- not found if not node or -- not found
rest == "" and node.is_dir or -- path is dir rest == "" and node.is_dir or -- path is dir
not node.proxy[method] then -- optional method not node.proxy[method] then -- optional method
@ -57,18 +66,18 @@ local function new_devfs_dir(name)
end end
-- proxy could be a file, which doesn't take an argument, but it can be ignored if passed -- proxy could be a file, which doesn't take an argument, but it can be ignored if passed
return node.proxy[method](rest) return node.proxy[method](rest)
end end
function sys.size(path) function sys:size(path)
return sys.invoke("size", path) return self.invoke("size", path)
end end
function sys.lastModified(path) function sys:lastModified(path)
return sys.invoke("lastModified", path) return self.invoke("lastModified", path)
end end
function sys.isDirectory(path) function sys:isDirectory(path)
local node, rest = sys.findNode(path) local node, rest = self.findNode(path)
if not node then if not node then
return return
end end
@ -78,15 +87,15 @@ local function new_devfs_dir(name)
elseif node.proxy then elseif node.proxy then
return node.proxy.isDirectory(rest) return node.proxy.isDirectory(rest)
end end
end end
function sys.open(path, mode) function sys:open(path, mode)
checkArg(1, path, "string") checkArg(1, path, "string")
checkArg(2, mode, "string", "nil") checkArg(2, mode, "string", "nil")
if not sys.exists(path) then if not self.exists(path) then
return nil, path.." file not found" return nil, path.." file not found"
elseif sys.isDirectory(path) then elseif self.isDirectory(path) then
return nil, path.." is a directory" return nil, path.." is a directory"
end end
@ -98,7 +107,7 @@ local function new_devfs_dir(name)
return nil, "invalid mode" return nil, "invalid mode"
end end
local node, rest = sys.findNode(path) local node, rest = self.findNode(path)
-- there must be a node, else exists would have failed -- there must be a node, else exists would have failed
local args = {} local args = {}
@ -109,10 +118,10 @@ local function new_devfs_dir(name)
args[#args+1] = mode args[#args+1] = mode
return node.proxy.open(table.unpack(args)) return node.proxy.open(table.unpack(args))
end end
function sys.list(path) function sys:list(path)
local node, rest = sys.findNode(path) local node, rest = self.findNode(path)
if not node or (rest ~= "" and not node.is_dir) then-- not found if not node or (rest ~= "" and not node.is_dir) then-- not found
return {} return {}
elseif rest == "" and not node.is_dir then -- path is file elseif rest == "" and not node.is_dir then -- path is file
@ -124,13 +133,15 @@ local function new_devfs_dir(name)
-- rest == "" and node.is_dir -- rest == "" and node.is_dir
local keys = {} local keys = {}
for k in pairs(node.children) do for k,node in pairs(node.children) do
if isAvailable(node) then
table.insert(keys, k) table.insert(keys, k)
end end
return keys
end end
return keys
end
function sys.remove(path) function sys:remove(path)
return nil, "cannot remove devfs files or directories" return nil, "cannot remove devfs files or directories"
--checkArg(1, path, "string") --checkArg(1, path, "string")
@ -138,22 +149,22 @@ local function new_devfs_dir(name)
-- return nil, "no such file or directory" -- return nil, "no such file or directory"
--end --end
--if not sys.exists(path) then --if not self.exists(path) then
-- return nil, path.." file not found" -- return nil, path.." file not found"
--end --end
--local node, rest = sys.findNode(path) --local node, rest = self.findNode(path)
--if rest ~= "" then -- if rest is not resolved, this isn't our path --if rest ~= "" then -- if rest is not resolved, this isn't our path
-- return node.proxy.remove(rest) -- return node.proxy.remove(rest)
--end --end
--node.parent.children[node.name] = nil --node.parent.children[node.name] = nil
end end
function sys.exists(path) function sys:exists(path)
checkArg(1, path, "string") checkArg(1, path, "string")
local node, rest = sys.findNode(path) local node, rest = self.findNode(path)
if not node then if not node then
return false return false
@ -162,10 +173,10 @@ local function new_devfs_dir(name)
else else
return node.proxy.exists(rest) return node.proxy.exists(rest)
end end
end end
function sys.create(path, handler) function sys:create(path, handler)
if sys.exists(path) then if self.exists(path) then
return nil, "path already exists" return nil, "path already exists"
end end
@ -177,21 +188,55 @@ local function new_devfs_dir(name)
return nil, "missing argument" return nil, "missing argument"
end end
local node, rest = sys.findNode(path, true) local node, rest = self.findNode(path, true)
if rest ~= "" then if rest ~= "" then
return node.proxy.create(rest, handler) return node.proxy.create(rest, handler)
end end
node.children[target] = new_node(node, target, not not handler.list, handler) node.children[target] = new_node(node, target, not not handler.list, handler)
return true return true
end end
return sys local function new_devfs_dir(name)
local sys_child = setmetatable({}, {__index=function(tbl,key)
if sys[key] then
return function(...)
return sys[key](tbl, ...)
end
end
end})
sys_child.mtab = new_node(nil, name or "/", true)
return sys_child
end end
local devfs = new_devfs_dir() local devfs = new_devfs_dir()
local bfd = "bad file descriptor" local bfd = "bad file descriptor"
-- to allow sub dirs to act like sub devfs
devfs.new_dir = new_devfs_dir
devfs.new_node = new_node
function devfs.new_callback_proxy(read_callback, write_callback)
return
{
open = function(mode)
if ({r=true, rb=true})[mode] then
if not read_callback then
return nil, "file cannot be opened for read"
end
return text.internal.reader(read_callback())
end
if not write_callback then
return nil, "file cannot be opened for write"
end
return text.internal.writer(write_callback, ({a=true,ab=true})[mode] and read_callback())
end,
size = function()
return read_callback and string.len(read_callback()) or 0
end
}
end
function devfs.setLabel(value) function devfs.setLabel(value)
error("drive does not support labeling") error("drive does not support labeling")
end end
@ -268,12 +313,15 @@ end
-- /dev is a special handler -- /dev is a special handler
local function devfs_load(key) local function devfs_load(key)
return require("tools/devfs/" .. key) -- using loadfile to allow us to pass args
-- load order complication: some dev points are dirs that want to use devfs api, but can't require devfs
devfs.create(key, loadfile("/lib/tools/devfs/" .. key .. ".lua", "bt", _G)(devfs))
end end
devfs.create("null", devfs_load("null")) devfs_load("random")
devfs.create("random", devfs_load("random")) devfs_load("null")
devfs.create("eeprom", devfs_load("eeprom")) devfs_load("eeprom")
devfs.create("eeprom-data", devfs_load("eeprom-data")) devfs_load("eeprom-data")
--devfs_load("filesystems")
return devfs return devfs

View File

@ -1,9 +1,12 @@
local computer = require("computer") local computer = require("computer")
local keyboard = require("keyboard") local keyboard = require("keyboard")
local event, listeners, timers = {}, {}, {} local event = {}
local handlers = {}
local lastInterrupt = -math.huge local lastInterrupt = -math.huge
event.handlers = handlers
local function call(callback, ...) local function call(callback, ...)
local result, message = pcall(callback, ...) local result, message = pcall(callback, ...)
if not result and type(event.onError) == "function" then if not result and type(event.onError) == "function" then
@ -13,42 +16,78 @@ local function call(callback, ...)
return message return message
end end
local function dispatch(signal, ...) function event.register(key, callback, interval, times)
if listeners[signal] then local handler =
local function callbacks() {
local list = {} key = key,
for index, listener in ipairs(listeners[signal]) do times = times or 0,
list[index] = listener callback = callback,
interval = interval or math.huge,
}
handler.timeout = computer.uptime() + handler.interval
if not interval then
handler.times = math.huge
end end
return list
end local id = 0
for _, callback in ipairs(callbacks()) do repeat
if call(callback, signal, ...) == false then id = id + 1
event.ignore(signal, callback) -- alternative method of removing a listener until not handlers[id]
handlers[id] = handler
return id
end
local function time_to_nearest()
local timeout = math.huge
for _,handler in pairs(handlers) do
if timeout > handler.timeout then
timeout = handler.timeout
end end
end end
return timeout
end
local function dispatch(...)
local signal = (...)
local eligable = {}
local ids_to_remove = {}
local time = computer.uptime()
for id, handler in pairs(handlers) do
-- timers have false keys
-- nil keys match anything
local key = handler.key
key = (key == nil and signal) or key
if key == signal or time >= handler.timeout then
-- push ticks to end of list (might be slightly faster to fire them last)
table.insert(eligable, select(handler.key and 1 or 2, 1, {handler.callback, id}))
handler.times = handler.times - 1
handler.timeout = computer.uptime() + handler.interval
if handler.times <= 0 then
table.insert(ids_to_remove, id)
end
end
end
for _,pack in ipairs(eligable) do
if call(pack[1], ...) == false then
table.insert(ids_to_remove, pack[2])
end
end
for _,id in ipairs(ids_to_remove) do
handlers[id] = nil
end end
end end
local function tick() local _pullSignal = computer.pullSignal
local function elapsed() computer.pullSignal = function(...)
local list = {} return (function(...)
for id, timer in pairs(timers) do dispatch(...)
if timer.after <= computer.uptime() then return ...
table.insert(list, timer.callback) end)(_pullSignal(...))
timer.times = timer.times - 1
if timer.times <= 0 then
timers[id] = nil
else
timer.after = computer.uptime() + timer.interval
end
end
end
return list
end
for _, callback in ipairs(elapsed()) do
call(callback)
end
end end
local function createPlainFilter(name, ...) local function createPlainFilter(name, ...)
@ -94,8 +133,8 @@ end
function event.cancel(timerId) function event.cancel(timerId)
checkArg(1, timerId, "number") checkArg(1, timerId, "number")
if timers[timerId] then if handlers[timerId] then
timers[timerId] = nil handlers[timerId] = nil
return true return true
end end
return false return false
@ -104,34 +143,24 @@ end
function event.ignore(name, callback) function event.ignore(name, callback)
checkArg(1, name, "string") checkArg(1, name, "string")
checkArg(2, callback, "function") checkArg(2, callback, "function")
if listeners[name] then for id, handler in pairs(handlers) do
for i = 1, #listeners[name] do if handler.key == name and handler.callback == callback then
if listeners[name][i] == callback then handlers[id] = nil
table.remove(listeners[name], i)
if #listeners[name] == 0 then
listeners[name] = nil
end
return true return true
end end
end end
end
return false return false
end end
function event.listen(name, callback) function event.listen(name, callback)
checkArg(1, name, "string") checkArg(1, name, "string")
checkArg(2, callback, "function") checkArg(2, callback, "function")
if listeners[name] then for id, handler in pairs(handlers) do
for i = 1, #listeners[name] do if handler.key == name and handler.callback == callback then
if listeners[name][i] == callback then
return false return false
end end
end end
else return event.register(name, callback, nil, nil)
listeners[name] = {}
end
table.insert(listeners[name], callback)
return true
end end
function event.onError(message) function event.onError(message)
@ -169,7 +198,6 @@ function event.pullMultiple(...)
end end
end end
return event.pullFiltered(seconds, createMultipleFilter(table.unpack(args, 1, args.n))) return event.pullFiltered(seconds, createMultipleFilter(table.unpack(args, 1, args.n)))
end end
function event.pullFiltered(...) function event.pullFiltered(...)
@ -187,15 +215,8 @@ function event.pullFiltered(...)
local deadline = seconds and (computer.uptime() + seconds) or math.huge local deadline = seconds and (computer.uptime() + seconds) or math.huge
repeat repeat
local closest = deadline local closest = math.min(deadline, time_to_nearest())
for _, timer in pairs(timers) do
closest = math.min(closest, timer.after)
end
local signal = table.pack(computer.pullSignal(closest - computer.uptime())) local signal = table.pack(computer.pullSignal(closest - computer.uptime()))
if signal.n > 0 then
dispatch(table.unpack(signal, 1, signal.n))
end
tick()
if event.shouldInterrupt() then if event.shouldInterrupt() then
lastInterrupt = computer.uptime() lastInterrupt = computer.uptime()
error("interrupted", 0) error("interrupted", 0)
@ -230,20 +251,12 @@ function event.timer(interval, callback, times)
checkArg(1, interval, "number") checkArg(1, interval, "number")
checkArg(2, callback, "function") checkArg(2, callback, "function")
checkArg(3, times, "number", "nil") checkArg(3, times, "number", "nil")
local id return event.register(false, callback, interval, times)
repeat
id = math.floor(math.random(1, 0x7FFFFFFF))
until not timers[id]
timers[id] = {
interval = interval,
after = computer.uptime() + interval,
callback = callback,
times = times or 1
}
return id
end end
-- users may expect to find event.push to exist -- users may expect to find event.push to exist
event.push = computer.pushSignal event.push = computer.pushSignal
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
return event return event

View File

@ -137,7 +137,7 @@ function require(module)
error(table.concat(errorMsg, "\n"), 2) error(table.concat(errorMsg, "\n"), 2)
end end
else else
error("already loading: " .. module .. debug.traceback(), 2) error("already loading: " .. module .. "\n" .. debug.traceback(), 2)
end end
end end

View File

@ -126,13 +126,13 @@ function plib.internal.redirectRead(pm)
return reader return reader
end end
function plib.internal.create(fp) function plib.internal.create(fp, init, name)
local _co = process.info().data.coroutine_handler local _co = process.info().data.coroutine_handler
local pco = setmetatable( local pco = setmetatable(
{ {
stack = {}, stack = {},
next = nil, next = false,
create = _co.create, create = _co.create,
wrap = _co.wrap, wrap = _co.wrap,
previous_handler = _co previous_handler = _co
@ -161,12 +161,12 @@ function plib.internal.create(fp)
return _co.yield(...) return _co.yield(...)
end end
function pco.set_unwind(from) function pco.set_unwind(from)
pco.next = nil pco.next = false
if from then if from then
local index = pco.index_of(from) local index = pco.index_of(from)
if index then if index then
pco.stack = tx.sub(pco.stack, 1, index-1) pco.stack = tx.sub(pco.stack, 1, index-1)
pco.next = pco.stack[index-1] pco.next = pco.stack[index-1] or false
end end
end end
end end
@ -212,7 +212,7 @@ function plib.internal.create(fp)
return true, _co.yield(...) -- pass args to resume next return true, _co.yield(...) -- pass args to resume next
else else
-- the stack is not running -- the stack is not running
pco.next = nil pco.next = false
local yield_args = table.pack(_co.resume(thread, ...)) local yield_args = table.pack(_co.resume(thread, ...))
if #pco.stack > 0 then if #pco.stack > 0 then
-- thread may have crashed (crash unwinds as well) -- thread may have crashed (crash unwinds as well)
@ -226,7 +226,7 @@ function plib.internal.create(fp)
-- in such a case, yield out first, then resume where we left off -- in such a case, yield out first, then resume where we left off
if pco.next and pco.next ~= thread then if pco.next and pco.next ~= thread then
local next = pco.next local next = pco.next
pco.next = nil pco.next = false
return pco.resume(next, table.unpack(yield_args,2,yield_args.n)) return pco.resume(next, table.unpack(yield_args,2,yield_args.n))
end end
end end
@ -251,7 +251,7 @@ function plib.internal.create(fp)
end end
if fp then if fp then
pco.stack = {process.load(fp,nil,nil--[[init]],"pco root")} pco.stack = {process.load(fp,nil,init,name or "pco root")}
process.info(pco.stack[1]).data.coroutine_handler = pco process.info(pco.stack[1]).data.coroutine_handler = pco
end end
@ -329,10 +329,8 @@ function pipeManager.new(prog, mode, env)
pm.commands[pm.self_id] = {pm.handler, {}} pm.commands[pm.self_id] = {pm.handler, {}}
pm.root = function() pm.root = function()
local startup_args = {}
local reason local reason
pm.threads, reason = sh.internal.createThreads(pm.commands, {}, pm.env) pm.threads, reason = sh.internal.createThreads(pm.commands, pm.env, {[pm.prog_id]=table.pack(pm.env,pm.prog)})
if not pm.threads then if not pm.threads then
pm.dead = true pm.dead = true
@ -340,7 +338,6 @@ function pipeManager.new(prog, mode, env)
end end
pm.pipe = process.info(pm.threads[1]).data.io[1] pm.pipe = process.info(pm.threads[1]).data.io[1]
process.info(pm.threads[pm.prog_id]).data.args = {pm.env,pm.prog}
-- if we are the writer, we need args to resume prog -- if we are the writer, we need args to resume prog
if pm.mode == "w" then if pm.mode == "w" then

View File

@ -20,6 +20,12 @@ function process.findProcess(co)
end end
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
local function parent_data(pre, tbl, k, ...)
if tbl and k then
return parent_data(pre, tbl[k], ...)
end
return setmetatable(pre, {__index=tbl})
end
function process.load(path, env, init, name) function process.load(path, env, init, name)
checkArg(1, path, "string", "function") checkArg(1, path, "string", "function")
@ -28,6 +34,7 @@ function process.load(path, env, init, name)
checkArg(4, name, "string", "nil") checkArg(4, name, "string", "nil")
assert(type(path) == "string" or env == nil, "process cannot load function environemnts") assert(type(path) == "string" or env == nil, "process cannot load function environemnts")
name = name or ""
local p = process.findProcess() local p = process.findProcess()
if p then if p then
@ -37,31 +44,42 @@ function process.load(path, env, init, name)
local code = nil local code = nil
if type(path) == 'string' then if type(path) == 'string' then
local f, reason = io.open(path) code = function(...)
local fs, shell = require("filesystem"), require("shell")
local program, reason = shell.resolve(path, 'lua')
if not program then
if fs.isDirectory(shell.resolve(path)) then
io.stderr:write(path .. ": is a directory\n")
return 126
end
io.stderr:write(path .. ": " .. reason .. "\n")
return 127
end
os.setenv("_", program)
local f, reason = io.open(program)
if not f then if not f then
return nil, reason io.stderr:write("could not read '" .. program .. "': " .. tostring(reason) .. "\n")
return 1
end end
local reason local shabang = f:read(2)
if f:read(2) == "#!" then
local command = f:read() local command = f:read()
if require("text").trim(command) == "" then
reason = "no exec command"
else
code = function()
local result = table.pack(require("shell").execute(command, env, path))
if not result[1] then
error(result[2], 0)
else
return table.unpack(result, 1, result.n)
end
end
end
else
code, reason = loadfile(path, "t", env)
end
f:close() f:close()
if not code then if shabang == "#!" then
return nil, reason if require("text").trim(command or "") == "" then
return -- nothing to do
end
local result = table.pack(require("shell").execute(command, env, program, ...))
if not result[1] then
error(result[2])
end
return table.unpack(result)
end
command, reason = loadfile(program, "bt", env)
if not command then
io.stderr:write(program..(reason or ""):gsub("^[^:]*", "").."\n")
return 128
end
return command(...)
end end
else -- path is code else -- path is code
code = path code = path
@ -69,43 +87,46 @@ function process.load(path, env, init, name)
local thread = nil local thread = nil
thread = coroutine.create(function(...) thread = coroutine.create(function(...)
if init then
init()
end
-- pcall code so that we can remove it from the process list on exit -- pcall code so that we can remove it from the process list on exit
local result = local result =
{ {
xpcall(code, function(msg) xpcall(function(...)
if type(msg) == 'table' then return msg end os.setenv("_", name)
init = init or function(...) return ... end
return code(init(...))
end,
function(msg)
-- msg can be a custom error object
if type(msg) == 'table' then
if msg.reason ~= "terminated" then
io.stderr:write(msg.reason.."\n")
end
return msg.code
end
local stack = debug.traceback():gsub('^([^\n]*\n)[^\n]*\n[^\n]*\n','%1') local stack = debug.traceback():gsub('^([^\n]*\n)[^\n]*\n[^\n]*\n','%1')
return string.format('%s:\n%s', msg or '', stack) io.stderr:write(string.format('%s:\n%s', msg or '', stack))
return 128 -- syserr
end, ...) end, ...)
} }
process.internal.close(thread) process.internal.close(thread)
if not result[1] then --result[1] is false if the exception handler also crashed
-- msg can be a custom error object if not result[1] and type(result[2]) ~= "number" then
local msg = result[2] require("event").onError(string.format("process library exception handler crashed: %s", tostring(result[2])))
if type(msg) == 'table' then
if msg.reason~="terminated" then error(msg.reason,2) end
result={0,msg.code}
else
error(msg,2)
end end
end return select(2, table.unpack(result))
return select(2,table.unpack(result)) end, true)
end,true)
process.list[thread] = { process.list[thread] = {
path = path, path = path,
command = name, command = name,
env = env, env = env,
data = setmetatable( data = parent_data(
{ {
handles = {}, handles = {},
io = setmetatable({}, {__index=p and p.data and p.data.io or nil}), io = parent_data({}, p, "data", "io"),
coroutine_handler = setmetatable({}, {__index=p and p.data and p.data.coroutine_handler or nil}), coroutine_handler = parent_data({}, p, "data", "coroutine_handler"),
}, {__index=p and p.data or nil}), }, p, "data"),
parent = p, parent = p,
instances = setmetatable({}, {__mode="v"}) instances = setmetatable({}, {__mode="v"}),
} }
return thread return thread
end end

View File

@ -23,7 +23,6 @@ local local_env = {event=event,fs=fs,process=process,shell=shell,term=term,text=
sh.internal.globbers = {{"*",".*"},{"?","."}} sh.internal.globbers = {{"*",".*"},{"?","."}}
sh.internal.ec = {} sh.internal.ec = {}
sh.internal.ec.parseCommand = 127 sh.internal.ec.parseCommand = 127
sh.internal.ec.sysError = 128
sh.internal.ec.last = 0 sh.internal.ec.last = 0
function sh.getLastExitCode() function sh.getLastExitCode()
@ -211,26 +210,20 @@ function sh.internal.parseCommand(words)
if #words == 0 then if #words == 0 then
return nil return nil
end end
local evaluated_words = {} -- evaluated words
local ewords = {}
-- the arguments have < or > which require parsing for redirection
local has_tokens
for i=1,#words do for i=1,#words do
for _, arg in ipairs(sh.internal.evaluate(words[i])) do for _, arg in ipairs(sh.internal.evaluate(words[i])) do
table.insert(evaluated_words, arg) table.insert(ewords, arg)
has_tokens = has_tokens or arg:find("[<>]")
end end
end end
local eword = evaluated_words[1] return table.remove(ewords, 1), ewords, has_tokens
local possible_dir_path = shell.resolve(eword)
if possible_dir_path and fs.isDirectory(possible_dir_path) then
return nil, string.format("%s: is a directory", eword)
end
local program, reason = shell.resolve(eword, "lua")
if not program then
return nil, eword .. ": " .. reason
end
evaluated_words = tx.sub(evaluated_words, 2)
return program, evaluated_words
end end
function sh.internal.createThreads(commands, eargs, env) function sh.internal.createThreads(commands, env, start_args)
-- Piping data between programs works like so: -- Piping data between programs works like so:
-- program1 gets its output replaced with our custom stream. -- program1 gets its output replaced with our custom stream.
-- program2 gets its input replaced with our custom stream. -- program2 gets its input replaced with our custom stream.
@ -240,14 +233,23 @@ function sh.internal.createThreads(commands, eargs, env)
-- custom stream may have "redirect" entries for fallback/duplication. -- custom stream may have "redirect" entries for fallback/duplication.
local threads = {} local threads = {}
for i = 1, #commands do for i = 1, #commands do
local program, args = table.unpack(commands[i]) local program, c_args, c_has_tokens = table.unpack(commands[i])
local name, thread = tostring(program) local name, thread = tostring(program)
local thread_env = type(program) == "string" and env or nil local thread_env = type(program) == "string" and env or nil
local thread, reason = process.load(program, thread_env, function() local thread, reason = process.load(program, thread_env, function(...)
os.setenv("_", name) local cdata = process.info().data.command
local args, has_tokens, start_args = cdata.args, cdata.has_tokens, cdata.start_args
if has_tokens then
args = sh.internal.buildCommandRedirects(args)
end
sh.internal.concatn(args, start_args)
sh.internal.concatn(args, {...}, select('#', ...))
-- popen expects each process to first write an empty string -- popen expects each process to first write an empty string
-- this is required for proper thread order -- this is required for proper thread order
io.write('') io.write("")
return table.unpack(args, 1, args.n or #args)
end, name) end, name)
threads[i] = thread threads[i] = thread
@ -259,25 +261,20 @@ function sh.internal.createThreads(commands, eargs, env)
return nil, reason return nil, reason
end end
process.info(thread).data.args = args local pdata = process.info(thread).data
pdata.command =
{
args = c_args,
has_tokens = c_has_tokens,
start_args = start_args and start_args[i] or {}
}
end end
if #threads > 1 then if #threads > 1 then
sh.internal.buildPipeChain(threads) sh.internal.buildPipeChain(threads)
end end
for i = 1, #threads do
local thread = threads[i]
local args = process.info(thread).data.args
-- smart check if ios should be loaded
if tx.first(args, function(token) return token == "<" or token:find(">") end) then
args, reason = sh.internal.buildCommandRedirects(thread, args)
end
process.info(thread).data.args = tx.concat(args, eargs or {})
end
return threads return threads
end end
@ -285,25 +282,19 @@ function sh.internal.runThreads(threads)
local result = {} local result = {}
for i = 1, #threads do for i = 1, #threads do
-- Emulate CC behavior by making yields a filtered event.pull() -- Emulate CC behavior by making yields a filtered event.pull()
local thread, args = threads[i] local thread, args = threads[i], {}
while coroutine.status(thread) ~= "dead" do while coroutine.status(thread) ~= "dead" do
args = args or process.info(thread).data.args
result = table.pack(coroutine.resume(thread, table.unpack(args))) result = table.pack(coroutine.resume(thread, table.unpack(args)))
if coroutine.status(thread) ~= "dead" then if coroutine.status(thread) ~= "dead" then
args = sh.internal.handleThreadYield(result) args = sh.internal.handleThreadYield(result)
-- in case this was the end of the line, args is returned
result = args
if table.remove(args, 1) then if table.remove(args, 1) then
break -- in case this was the end of the line, args is returned
return args[2]
end end
end end
end end
if not result[1] then
sh.internal.handleThreadCrash(thread, result)
break
end end
end return result[2]
return table.unpack(result)
end end
function sh.internal.executePipes(pipe_parts, eargs, env) function sh.internal.executePipes(pipe_parts, eargs, env)
@ -318,23 +309,12 @@ function sh.internal.executePipes(pipe_parts, eargs, env)
return sh.internal.ec.parseCommand return sh.internal.ec.parseCommand
end end
end end
local threads, reason = sh.internal.createThreads(commands, eargs, env) local threads, reason = sh.internal.createThreads(commands, env, {[#commands]=eargs})
if not threads then if not threads then
io.stderr:write(reason,"\n") io.stderr:write(reason,"\n")
return false return false
end end
local result, cmd_result = sh.internal.runThreads(threads) return sh.internal.runThreads(threads)
if not result then
if cmd_result then
if type(cmd_result) == "string" then
cmd_result = cmd_result:gsub("^/lib/process%.lua:%d+: /", '/')
end
io.stderr:write(tostring(cmd_result),"\n")
end
return sh.internal.ec.sysError
end
return cmd_result
end end
function sh.execute(env, command, ...) function sh.execute(env, command, ...)
@ -347,7 +327,8 @@ function sh.execute(env, command, ...)
return true, 0 return true, 0
end end
local eargs = {...} -- MUST be table.pack for non contiguous ...
local eargs = table.pack(...)
-- simple -- simple
if reason then if reason then
@ -358,6 +339,15 @@ function sh.execute(env, command, ...)
return sh.internal.execute_complex(statements, eargs, env) return sh.internal.execute_complex(statements, eargs, env)
end end
function sh.internal.concatn(apack, bpack, bn)
local an = (apack.n or #apack)
bn = bn or bpack.n or #bpack
for i=1,bn do
apack[an + i] = bpack[i]
end
apack.n = an + bn
end
function --[[@delayloaded-start@]] sh.internal.handleThreadYield(result) function --[[@delayloaded-start@]] sh.internal.handleThreadYield(result)
local action = result[2] local action = result[2]
if action == nil or type(action) == "number" then if action == nil or type(action) == "number" then
@ -367,20 +357,7 @@ function --[[@delayloaded-start@]] sh.internal.handleThreadYield(result)
end end
end --[[@delayloaded-end@]] end --[[@delayloaded-end@]]
function --[[@delayloaded-start@]] sh.internal.handleThreadCrash(thread, result) function --[[@delayloaded-start@]] sh.internal.buildCommandRedirects(args, thread)
if type(result[2]) == "table" and result[2].reason == "terminated" then
if result[2].code then
result[1] = true
result.n = 1
else
result[2] = "terminated"
end
elseif type(result[2]) == "string" then
result[2] = debug.traceback(thread, result[2])
end
end --[[@delayloaded-end@]]
function --[[@delayloaded-start@]] sh.internal.buildCommandRedirects(thread, args)
local data = process.info(thread).data local data = process.info(thread).data
local tokens, ios, handles = args, data.io, data.handles local tokens, ios, handles = args, data.io, data.handles
args = {} args = {}
@ -406,7 +383,7 @@ function --[[@delayloaded-start@]] sh.internal.buildCommandRedirects(thread, arg
else else
local file, reason = io.open(shell.resolve(token), mode) local file, reason = io.open(shell.resolve(token), mode)
if not file then if not file then
return nil, "could not open '" .. token .. "': " .. reason error("could not open '" .. token .. "': " .. reason)
end end
table.insert(handles, file) table.insert(handles, file)
ios[from_io] = file ios[from_io] = file
@ -447,7 +424,6 @@ function --[[@delayloaded-start@]] sh.internal.buildPipeChain(threads)
prev_pipe = pipe prev_pipe = pipe
end end
end --[[@delayloaded-end@]] end --[[@delayloaded-end@]]
function --[[@delayloaded-start@]] sh.internal.glob(glob_pattern) function --[[@delayloaded-start@]] sh.internal.glob(glob_pattern)
@ -825,7 +801,7 @@ function --[[@delayloaded-start@]] sh.internal.newMemoryStream()
-- if that is the case, it is important to leave stream.buffer alone -- if that is the case, it is important to leave stream.buffer alone
return self.redirect[0]:read(n) return self.redirect[0]:read(n)
elseif self.buffer == "" then elseif self.buffer == "" then
process.info(self.next).data.args = table.pack(coroutine.yield(table.unpack(self.result))) coroutine.yield()
end end
local result = string.sub(self.buffer, 1, n) local result = string.sub(self.buffer, 1, n)
self.buffer = string.sub(self.buffer, n + 1) self.buffer = string.sub(self.buffer, n + 1)
@ -842,15 +818,13 @@ function --[[@delayloaded-start@]] sh.internal.newMemoryStream()
return self.redirect[1]:write(value) return self.redirect[1]:write(value)
elseif not self.closed then elseif not self.closed then
self.buffer = self.buffer .. value self.buffer = self.buffer .. value
local args = process.info(self.next).data.args local result = table.pack(coroutine.resume(self.next))
self.result = table.pack(coroutine.resume(self.next, table.unpack(args)))
if coroutine.status(self.next) == "dead" then if coroutine.status(self.next) == "dead" then
self:close() self:close()
end end
if not self.result[1] then if not result[1] then
error(self.result[2], 0) error(result[2], 0)
end end
table.remove(self.result, 1)
return self return self
end end
os.exit(0) -- abort the current process: SIGPIPE os.exit(0) -- abort the current process: SIGPIPE

View File

@ -3,6 +3,7 @@ local event = require("event")
local process = require("process") local process = require("process")
local kb = require("keyboard") local kb = require("keyboard")
local component = require("component") local component = require("component")
local computer = require("computer")
local keys = kb.keys local keys = kb.keys
local term = {} local term = {}
@ -35,8 +36,11 @@ end
function term.setViewport(w,h,dx,dy,x,y,window) function term.setViewport(w,h,dx,dy,x,y,window)
window = window or W() window = window or W()
dx,dy,x,y = dx or 0,dy or 0,x or 1,y or 1
if not w or not h then
local gw,gh = window.gpu.getViewport() local gw,gh = window.gpu.getViewport()
w,h,dx,dy,x,y = w or gw,h or gh,dx or 0,dy or 0,x or 1,y or 1 w,h = w or gw, h or gh
end
window.w,window.h,window.dx,window.dy,window.x,window.y,window.gw,window.gh= window.w,window.h,window.dx,window.dy,window.x,window.y,window.gw,window.gh=
w,h,dx,dy,x,y, gw, gh w,h,dx,dy,x,y, gw, gh
@ -60,57 +64,79 @@ function term.isAvailable(w)
return w and not not (w.gpu and w.gpu.getScreen()) return w and not not (w.gpu and w.gpu.getScreen())
end end
function term.internal.pull(input, c, off, t, ...) function term.internal.pull(input, timeout, ...)
t=t or math.huge timeout = timeout or math.huge
if t < 0 then return end
local w,unpack=W(),table.unpack local w = W()
local d,h,dx,dy,x,y=term.getViewport(w) local d, h, dx, dy, x, y = term.getViewport(w)
local out = (x<1 or x>d or y<1 or y>h) local out = (x<1 or x>d or y<1 or y>h)
if input and out then if input and out then
input:move(0) input:move(0)
y=w.y y = w.y
input:scroll() input:scroll()
end end
x,y=w.x+dx,w.y+dy
local gpu
if input or not out then x, y = w.x + dx, w.y + dy
gpu=w.gpu local gpu = (input or not out) and w.gpu
local sf,sb=gpu.setForeground,gpu.setBackground
c=c or {{gpu.getBackground()},{gpu.getForeground()},gpu.get(x,y)} local bgColor, bgIsPalette
local c11,c12 = unpack(c[1]) local fgColor, fgIsPalette
local c21,c22 = unpack(c[2]) local char_at_cursor
-- c can fail if gpu does not have a screen local blinking
-- if can happen during a type of race condition when a screen is removed if gpu then
if not c11 then bgColor, bgIsPalette = gpu.getBackground()
-- it can happen during a type of race condition when a screen is removed
if not bgColor then
return nil, "interrupted" return nil, "interrupted"
end end
if not off then
sf(c11,c12) fgColor, fgIsPalette = gpu.getForeground()
sb(c21,c22) char_at_cursor = gpu.get(x, y)
blinking = w.blink
if input then
blinking = input.blink
end end
gpu.set(x,y,c[3])
sb(c11,c12)
sf(c21,c22)
end end
local a={event.pull(math.min(t,0.5),...)} -- get the next event
local blinked = false
if #a>1 or t<.5 then local done = false
local signal
while true do
if gpu then if gpu then
gpu.set(x,y,c[3]) if not blinked and not done then
gpu.setForeground(bgColor, bgIsPalette)
gpu.setBackground(fgColor, fgIsPalette)
gpu.set(x, y, char_at_cursor)
gpu.setForeground(fgColor, fgIsPalette)
gpu.setBackground(bgColor, bgIsPalette)
blinked = true
elseif blinked then
gpu.set(x, y, char_at_cursor)
blinked = false
end end
return unpack(a)
end end
local blinking = w.blink
if input then blinking = input.blink end if done then
return term.internal.pull(input,c,blinking and not off,t-0.5,...) return table.unpack(signal, 1, signal.n)
end
signal = table.pack(event.pull(math.min(.5, timeout), ...))
timeout = timeout - .5
done = signal.n > 1 or timeout < .5
end
end end
function term.pull(p,...) function term.pull(...)
local a,t = {p,...} local args = table.pack(...)
if type(p) == "number" then t = table.remove(a,1) end local timeout = nil
return term.internal.pull(nil,nil,nil,t,table.unpack(a)) if type(args[1]) == "number" then
timeout = table.remove(args, 1)
args.n = args.n - 1
end
return term.internal.pull(nil, timeout, table.unpack(args, 1, args.n))
end end
function term.read(history,dobreak,hintHandler,pwchar,filter) function term.read(history,dobreak,hintHandler,pwchar,filter)

View File

@ -0,0 +1,146 @@
-- called from /init.lua
local raw_loadfile = ...
_G._OSVERSION = "OpenOS 1.6"
local component = component
local computer = computer
local unicode = unicode
-- Runlevel information.
local runlevel, shutdown = "S", computer.shutdown
computer.runlevel = function() return runlevel end
computer.shutdown = function(reboot)
runlevel = reboot and 6 or 0
if os.sleep then
computer.pushSignal("shutdown")
os.sleep(0.1) -- Allow shutdown processing.
end
shutdown(reboot)
end
local screen = component.list('screen', true)()
for address in component.list('screen', true) do
if #component.invoke(address, 'getKeyboards') > 0 then
screen = address
break
end
end
_G.boot_screen = screen
-- Report boot progress if possible.
local gpu = component.list("gpu", true)()
local w, h
if gpu and screen then
component.invoke(gpu, "bind", screen)
w, h = component.invoke(gpu, "maxResolution")
component.invoke(gpu, "setResolution", w, h)
component.invoke(gpu, "setBackground", 0x000000)
component.invoke(gpu, "setForeground", 0xFFFFFF)
component.invoke(gpu, "fill", 1, 1, w, h, " ")
end
local y = 1
local function status(msg)
if gpu and screen then
component.invoke(gpu, "set", 1, y, msg)
if y == h then
component.invoke(gpu, "copy", 1, 2, w, h - 1, 0, -1)
component.invoke(gpu, "fill", 1, h, w, 1, " ")
else
y = y + 1
end
end
end
status("Booting " .. _OSVERSION .. "...")
-- Custom low-level dofile implementation reading from our ROM.
local loadfile = function(file)
status("> " .. file)
return raw_loadfile(file)
end
local function dofile(file)
local program, reason = loadfile(file)
if program then
local result = table.pack(pcall(program))
if result[1] then
return table.unpack(result, 2, result.n)
else
error(result[2])
end
else
error(reason)
end
end
status("Initializing package management...")
-- Load file system related libraries we need to load other stuff moree
-- comfortably. This is basically wrapper stuff for the file streams
-- provided by the filesystem components.
local package = dofile("/lib/package.lua")
do
-- Unclutter global namespace now that we have the package module.
_G.component = nil
_G.computer = nil
_G.process = nil
_G.unicode = nil
-- Initialize the package module with some of our own APIs.
package.loaded.component = component
package.loaded.computer = computer
package.loaded.unicode = unicode
package.preload["buffer"] = loadfile("/lib/buffer.lua")
package.preload["filesystem"] = loadfile("/lib/filesystem.lua")
-- Inject the package and io modules into the global namespace, as in Lua.
_G.package = package
_G.io = loadfile("/lib/io.lua")()
--mark modules for delay loaded api
package.delayed["text"] = true
package.delayed["sh"] = true
package.delayed["transforms"] = true
package.delayed["term"] = true
end
status("Initializing file system...")
-- Mount the ROM and temporary file systems to allow working on the file
-- system module from this point on.
require("filesystem").mount(computer.getBootAddress(), "/")
package.preload={}
status("Running boot scripts...")
-- Run library startup scripts. These mostly initialize event handlers.
local function rom_invoke(method, ...)
return component.invoke(computer.getBootAddress(), method, ...)
end
local scripts = {}
for _, file in ipairs(rom_invoke("list", "boot")) do
local path = "boot/" .. file
if not rom_invoke("isDirectory", path) then
table.insert(scripts, path)
end
end
table.sort(scripts)
for i = 1, #scripts do
dofile(scripts[i])
end
status("Initializing components...")
for c, t in component.list() do
computer.pushSignal("component_added", c, t)
end
status("Initializing system...")
computer.pushSignal("init") -- so libs know components are initialized.
require("event").pull(1, "init") -- Allow init processing.
_G.runlevel = 1

View File

@ -1,15 +1,10 @@
local comp = require("component") local comp = require("component")
local text = require("text") local devfs = ...
return -- eeprom get/set has to delayed because comp.eeprom may not be available
{ local node = devfs.new_callback_proxy(function() return comp.eeprom.getData() end, function(...) comp.eeprom.setData(...) end)
open = function(mode) function node.isAvailable()
if ({r=true, rb=true})[mode] then return comp.list("eeprom", true)()
return text.internal.reader(comp.eeprom.getData()) end
end
return text.internal.writer(comp.eeprom.setData, ({a=true,ab=true})[mode] and comp.eeprom.getData()) return node
end,
size = function()
return string.len(comp.eeprom.getData())
end
}

View File

@ -1,15 +1,10 @@
local comp = require("component") local comp = require("component")
local text = require("text") local devfs = ...
return -- eeprom get/set has to delayed because comp.eeprom may not be available
{ local node = devfs.new_callback_proxy(function() return comp.eeprom.get() end, function(...) comp.eeprom.set(...) end)
open = function(mode) function node.isAvailable()
if ({r=true, rb=true})[mode] then return comp.list("eeprom", true)()
return text.internal.reader(comp.eeprom.get()) end
end
return text.internal.writer(comp.eeprom.set, ({a=true,ab=true})[mode] and comp.eeprom.get()) return node
end,
size = function()
return string.len(comp.eeprom.get())
end
}

View File

@ -162,7 +162,7 @@ end
local function wide(n,i) local function wide(n,i)
local t = _isLink(n,i) and 'l' or _isDir(n,i) and 'd' or 'f' local t = _isLink(n,i) and 'l' or _isDir(n,i) and 'd' or 'f'
local link_target = _isLink(n,i) and local link_target = _isLink(n,i) and
string.format(" -> %s",_linkPath(n,i)..(_isDir(n,i)and"/"or""))or"" string.format(" -> %s", _linkPath(n, i):gsub("/+$", "") .. (_isDir(n, i) and "/" or "")) or ""
local w = fs.get(_fullPath(n,i)).isReadOnly() and '-' or 'w' local w = fs.get(_fullPath(n,i)).isReadOnly() and '-' or 'w'
local size = formatSize(_size(n,i)) local size = formatSize(_size(n,i))
local modDate = formatDate(_time(n,i)) local modDate = formatDate(_time(n,i))

View File

@ -1 +1,3 @@
os.execute(install.from.."/oppm.lua") require("shell").setWorkingDirectory(install.from)
os.execute("oppm.lua")