Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- if _G.polychoron ~= nil then
- if fs.exists "autorun" then shell.run "autorun" end
- -- exit now
- -- as we are running inside it already
- return
- end
- local version = "1.4"
- -- Localize frequently used functions for performance
- local osepoch = os.epoch
- local osclock = os.clock
- local stringformat = string.format
- local coroutineresume = coroutine.resume
- local coroutineyield = coroutine.yield
- local coroutinestatus = coroutine.status
- local tostring = tostring
- local ccemuxnanoTime
- if ccemux then
- ccemuxnanoTime = ccemux.nanoTime
- end
- -- Return a time of some sort. Not used to provide "objective" time measurement, just for duration comparison
- local function time()
- if ccemuxnanoTime then
- return ccemuxnanoTime() / 1e9
- elseif osepoch then
- return osepoch "utc" / 1000 else
- return os.clock() end
- end
- local processes = {}
- _G.process = {}
- -- Allow getting processes by name, and nice process views from process.list()
- local process_list_mt = {
- __tostring = function(ps)
- local o = ""
- for _, p in pairs(ps) do
- o = o .. tostring(p)
- o = o .. "\n"
- end
- return o:gsub("\n$", "") -- strip trailing newline
- end,
- __index = function(tabl, key)
- for i, p in pairs(tabl) do
- if p.name == key then return p end
- end
- end
- }
- setmetatable(processes, process_list_mt)
- -- To make suspend kind of work with sleep, we need to bodge it a bit
- -- So this modified sleep *also* checks the time, in case timer events were eaten
- function _G.sleep(time)
- time = time or 0
- local t = os.startTimer(time)
- local start = os.clock()
- local ev, arg, tdiff
- repeat
- ev, arg = os.pullEvent()
- until (ev == "timer" and arg == t) or (os.clock() - start) > time
- end
- process.statuses = {
- DEAD = "dead",
- ERRORED = "errored",
- OK = "ok",
- STOPPED = "stopped"
- }
- process.signals = {
- START = "start",
- STOP = "stop",
- TERMINATE = "terminate",
- KILL = "kill"
- }
- -- Gets the first key in a table with the given value
- local function get_key_with_value(t, v)
- for tk, tv in pairs(t) do
- if v == tv then
- return tk
- end
- end
- end
- -- Contains custom stringification, and an equality thing using IDs
- local process_metatable = {
- __tostring = function(p)
- local text = stringformat("[process %d %s: %s", p.ID, p.name or "[unnamed]", get_key_with_value(process.statuses, p.status) or "?")
- if p.parent then
- text = text .. stringformat("; parent %s", p.parent.name or p.parent.ID)
- end
- return text .. "]"
- end,
- __eq = function(p1, p2)
- return p1.ID == p2.ID
- end
- }
- -- Whitelist of events which ignore filters.
- local allow_event = {
- terminate = true
- }
- local function process_to_info(p)
- if not p then return nil end
- local out = {}
- for k, v in pairs(p) do
- if k == "parent" and v ~= nil then
- out.parent = process_to_info(v)
- else
- -- PS#85DD8AFC
- -- Through some bizarre environment weirdness even exposing the function causes security risks. So don't.
- if k ~= "coroutine" and k ~= "function" then
- out[k] = v
- end
- end
- end
- setmetatable(out, process_metatable)
- return out
- end
- -- Fancy BSOD
- local function BSOD(e)
- if term.isColor() then term.setBackgroundColor(colors.blue) term.setTextColor(colors.white)
- else term.setBackgroundColor(colors.white) term.setTextColor(colors.black) end
- term.clear()
- term.setCursorBlink(false)
- term.setCursorPos(1, 1)
- print(e)
- end
- local running
- -- Apply "event" to "proc"
- -- Where most important stuff happens
- local function tick(proc, event)
- if not proc then error "No such process" end
- if process.running and process.running.ID == proc.ID then return end
- -- Run any given event preprocessor on the event
- -- Actually don't, due to (hypothetical) PS#D7CD76C0-like exploits
- --[[
- if type(proc.event_preprocessor) == "function" then
- event = proc.event_preprocessor(event)
- if event == nil then return end
- end
- ]]
- -- If coroutine is dead, just ignore it but set its status to dead
- if coroutinestatus(proc.coroutine) == "dead" then
- proc.status = process.statuses.DEAD
- if proc.ephemeral then
- processes[proc.ID] = nil
- end
- end
- -- If coroutine ready and filter matches or event is allowed, run it, set the running process in its environment,
- -- get execution time, and run error handler if errors happen.
- if proc.status == process.statuses.OK and (proc.filter == nil or proc.filter == event[1] or allow_event[event[1]]) then
- process.running = process_to_info(proc)
- running = proc
- local start_time = time()
- local ok, res = coroutineresume(proc.coroutine, table.unpack(event))
- local end_time = time()
- proc.execution_time = end_time - start_time
- if not ok then
- if proc.error_handler then
- proc.error_handler(res)
- else
- proc.status = process.statuses.ERRORED
- proc.error = res
- if res ~= "Terminated" then -- programs terminating is normal, other errors not so much
- BSOD(stringformat("Process %s has crashed!\nError: %s", tostring(proc.ID) or proc.name, tostring(res)))
- end
- end
- else
- proc.filter = res
- end
- process.running = nil
- end
- end
- function process.get_running()
- return running
- end
- -- Send/apply the given signal to the given process
- local function apply_signal(proc, signal)
- local rID = nil
- if process.running then rID = process.running.ID end
- tick(proc, { "signal", signal, rID })
- -- START - starts stopped process
- if signal == process.signals.START and proc.status == process.statuses.STOPPED then
- proc.status = process.statuses.OK
- -- STOP stops started process
- elseif signal == process.signals.STOP and proc.status == process.statuses.OK then
- proc.status = process.statuses.STOPPED
- elseif signal == process.signals.TERMINATE then
- proc.terminated_time = os.clock()
- tick(proc, { "terminate" })
- elseif signal == process.signals.KILL then
- proc.status = process.statuses.DEAD
- end
- end
- local next_ID = 1
- function process.spawn(fn, name, extra)
- local this_ID = next_ID
- local proc = {
- coroutine = coroutine.create(fn),
- name = name,
- status = process.statuses.OK,
- ID = this_ID,
- parent = process.running,
- ["function"] = fn
- }
- if extra then for k, v in pairs(extra) do proc[k] = v end end
- setmetatable(proc, process_metatable)
- processes[this_ID] = proc
- next_ID = next_ID + 1
- return this_ID
- end
- function process.thread(fn, name)
- local parent = process.running.name or tostring(process.running.ID)
- process.spawn(fn, ("%s_%s_%04x"):format(name or "thread", parent, math.random(0, 0xFFFF)), { ephemeral = true })
- end
- -- Sends a signal to the given process ID
- function process.signal(ID, signal)
- if not processes[ID] then error(stringformat("No such process %s.", tostring(ID))) end
- apply_signal(processes[ID], signal)
- end
- -- PS#F7686798
- -- Prevent mutation of processes through exposed API to prevent PS#D7CD76C0-like exploits
- -- List all processes
- function process.list()
- local out = {}
- for k, v in pairs(processes) do
- out[k] = process_to_info(v)
- end
- return setmetatable(out, process_list_mt)
- end
- function process.info(ID)
- return process_to_info(processes[ID])
- end
- -- Run main event loop
- local function run_loop()
- while true do
- local ev = {coroutineyield()}
- for ID, proc in pairs(processes) do
- tick(proc, ev)
- end
- end
- end
- local base_processes = {
- ["shell"] = function() os.run({}, "/rom/programs/shell.lua") end,
- ["rednetd"] = function()
- -- bodge, because of the stupid rednet bRunning thing
- local old_error = error
- _G.error = function() _G.error = old_error end
- rednet.run()
- end
- }
- -- hacky magic to run our code and not the BIOS stuff
- -- this terminates the shell, which crashes the BIOS, which then causes an error, which is printed with printError
- local old_printError = _G.printError
- function _G.printError()
- _G.printError = old_printError
- -- Multishell must die.
- term.redirect(term.native())
- multishell = nil
- term.setTextColor(colors.yellow)
- term.setBackgroundColor(colors.black)
- term.setCursorPos(1,1)
- term.clear()
- _G.polychoron = {version = version, process = process}
- polychoron.polychoron = polychoron
- polychoron.BSOD = BSOD
- for n, p in pairs(base_processes) do
- process.spawn(p, n)
- end
- os.queueEvent "event" -- so that processes get one free "tick"
- run_loop()
- end
- os.queueEvent "terminate"
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement