diff --git a/sys/apps/Sniff.lua b/sys/apps/Sniff.lua index 5336234..41c1543 100644 --- a/sys/apps/Sniff.lua +++ b/sys/apps/Sniff.lua @@ -255,7 +255,7 @@ function page.packetSlide:eventHandler(event) page:setFocus(page.packetGrid) elseif event.type == 'packet_lua' then - multishell.openTab({ path = 'sys/apps/Lua.lua', args = { self.currentPacket.message }, focused = true }) + multishell.openTab(_ENV, { path = 'sys/apps/Lua.lua', args = { self.currentPacket.message }, focused = true }) elseif event.type == 'prev_packet' then local c = self.currentPacket diff --git a/sys/apps/Tasks.lua b/sys/apps/Tasks.lua index be21f9c..4e359fc 100644 --- a/sys/apps/Tasks.lua +++ b/sys/apps/Tasks.lua @@ -12,6 +12,7 @@ local page = UI.Page { buttons = { { text = 'Activate', event = 'activate' }, { text = 'Terminate', event = 'terminate' }, + { text = 'Inspect', event = 'inspect' }, }, }, grid = UI.ScrollingGrid { @@ -49,6 +50,12 @@ local page = UI.Page { multishell.setFocus(t.uid) elseif event.type == 'terminate' then multishell.terminate(t.uid) + elseif event.type == 'inspect' then + multishell.openTab(_ENV, { + path = 'sys/apps/Lua.lua', + args = { t }, + focused = true, + }) end end if event.type == 'quit' then diff --git a/sys/apps/inspect.lua b/sys/apps/inspect.lua index 0df9526..90b59db 100644 --- a/sys/apps/inspect.lua +++ b/sys/apps/inspect.lua @@ -110,7 +110,7 @@ page = UI.Page { self.grid:draw() elseif event.type == 'grid_select' then - multishell.openTab({ + multishell.openTab(_ENV, { path = 'sys/apps/Lua.lua', args = { event.selected.raw }, focused = true, diff --git a/sys/apps/netdaemon.lua b/sys/apps/netdaemon.lua index 1d3fd66..8ffca25 100644 --- a/sys/apps/netdaemon.lua +++ b/sys/apps/netdaemon.lua @@ -1,5 +1,3 @@ -_G.requireInjector(_ENV) - local Event = require('opus.event') local Util = require('opus.util') diff --git a/sys/apps/network/snmp.lua b/sys/apps/network/snmp.lua index 18a4143..2c48ee1 100644 --- a/sys/apps/network/snmp.lua +++ b/sys/apps/network/snmp.lua @@ -31,21 +31,14 @@ local function snmpConnection(socket) socket:write('pong') elseif msg.type == 'script' then - local env = kernel.makeEnv() - local fn, err = load(msg.args, 'script', nil, env) - if fn then - kernel.run({ - fn = fn, - env = env, - title = 'script', - }) - else - _G.printError(err) - end + kernel.run(_ENV, { + chunk = msg.args, + title = 'script', + }) elseif msg.type == 'scriptEx' then local s, m = pcall(function() - local env = kernel.makeEnv() + local env = kernel.makeEnv(_ENV) local fn, m = load(msg.args, 'script', nil, env) if not fn then error(m) diff --git a/sys/apps/network/telnet.lua b/sys/apps/network/telnet.lua index 87ab04d..ff92298 100644 --- a/sys/apps/network/telnet.lua +++ b/sys/apps/network/telnet.lua @@ -41,12 +41,12 @@ local function telnetHost(socket, mode) end end - local shellThread = kernel.run({ + local shellThread = kernel.run(_ENV, { window = win, title = mode .. ' client', hidden = true, fn = function() - Util.run(kernel.makeEnv(), Alt.get('shell'), table.unpack(termInfo.program)) + Util.run(kernel.makeEnv(_ENV), Alt.get('shell'), table.unpack(termInfo.program)) if socket.queue then socket:write(socket.queue) end diff --git a/sys/apps/shell.lua b/sys/apps/shell.lua index 6b23582..92cf23d 100644 --- a/sys/apps/shell.lua +++ b/sys/apps/shell.lua @@ -6,16 +6,6 @@ local fs = _G.fs local settings = _G.settings local shell = _ENV.shell -local sandboxEnv = { } -for k,v in pairs(_ENV) do - sandboxEnv[k] = v -end -sandboxEnv.package = nil -sandboxEnv.require = nil -sandboxEnv.arg = nil -sandboxEnv._ENV = nil -sandboxEnv.shell = shell - _G.requireInjector(_ENV) local trace = require('opus.trace') @@ -51,7 +41,7 @@ local function run(...) local args = tokenise(...) local command = table.remove(args, 1) or error('No such program') local isUrl = not not command:match("^(https?:)") - local env = shell.makeEnv() + local env = shell.makeEnv(_ENV) local path, loadFn if isUrl then @@ -64,7 +54,7 @@ local function run(...) local fn, err = loadFn(path, env) if not fn then - error(err) + error(err, -1) end if _ENV.multishell then @@ -287,13 +277,9 @@ function shell.getRunningInfo() return tProgramStack[#tProgramStack] end -function shell.setEnv(name, value) - _ENV[name] = value - sandboxEnv[name] = value -end - -function shell.makeEnv() - local env = setmetatable(Util.shallowCopy(sandboxEnv), { __index = _G }) +-- convenience function for making a runnable env +function shell.makeEnv(env) + env = setmetatable(Util.shallowCopy(env), { __index = _G }) _G.requireInjector(env) return env end @@ -321,7 +307,6 @@ function shell.newTab(tabInfo, ...) if path then tabInfo.path = path - tabInfo.env = shell.makeEnv() tabInfo.args = args tabInfo.title = fs.getName(path):match('([^%.]+)') @@ -329,25 +314,21 @@ function shell.newTab(tabInfo, ...) table.insert(tabInfo.args, 1, tabInfo.path) tabInfo.path = 'sys/apps/shell.lua' end - return _ENV.multishell.openTab(tabInfo) + return _ENV.multishell.openTab(_ENV, tabInfo) end return nil, 'No such program' end -function shell.openTab(...) - -- needs to use multishell.launch .. so we can run with stock multishell - local tWords = tokenise( ... ) - local sCommand = tWords[1] - if sCommand then - local sPath = shell.resolveProgram(sCommand) - if sPath == "sys/apps/shell.lua" then - return _ENV.multishell.launch(shell.makeEnv(), sPath, table.unpack(tWords, 2)) - else - return _ENV.multishell.launch(shell.makeEnv(), "sys/apps/shell.lua", sCommand, table.unpack(tWords, 2)) - end +if not _ENV.multishell then + function shell.newTab() + error('Multishell is not available') end end +function shell.openTab(...) + return shell.newTab({ }, ...) +end + function shell.openForegroundTab( ... ) return shell.newTab({ focused = true }, ...) end diff --git a/sys/autorun/hotkeys.lua b/sys/autorun/hotkeys.lua index ab591e0..ab65ad6 100644 --- a/sys/autorun/hotkeys.lua +++ b/sys/autorun/hotkeys.lua @@ -10,9 +10,8 @@ if multishell and multishell.getTabs then local tab = kernel.getFocused() if tab and not tab.noTerminate then multishell.terminate(tab.uid) - multishell.openTab({ + multishell.openTab(tab.env, { path = tab.path, - env = tab.env, args = tab.args, focused = true, }) diff --git a/sys/autorun/log.lua b/sys/autorun/log.lua index 3540f0d..c43c1dc 100644 --- a/sys/autorun/log.lua +++ b/sys/autorun/log.lua @@ -39,7 +39,7 @@ local function systemLog() keyboard.removeHotkey('control-d') end -kernel.run({ +kernel.run(_ENV, { title = 'System Log', fn = systemLog, noTerminate = true, diff --git a/sys/init/5.network.lua b/sys/init/5.network.lua index d526b62..8e31fa4 100644 --- a/sys/init/5.network.lua +++ b/sys/init/5.network.lua @@ -15,7 +15,7 @@ do end local function startNetwork() - kernel.run({ + kernel.run(_ENV, { title = 'Net daemon', path = 'sys/apps/netdaemon.lua', hidden = true, diff --git a/sys/init/7.multishell.lua b/sys/init/7.multishell.lua index f37033c..3d2f0ba 100644 --- a/sys/init/7.multishell.lua +++ b/sys/init/7.multishell.lua @@ -8,7 +8,6 @@ local kernel = _G.kernel local keys = _G.keys local os = _G.os local printError = _G.printError -local shell = _ENV.shell local window = _G.window local parentTerm = _G.device.terminal @@ -18,7 +17,7 @@ local tabsDirty = false local closeInd = Util.getVersion() >= 1.76 and '\215' or '*' local multishell = { } -shell.setEnv('multishell', multishell) +_ENV.multishell = multishell kernel.window.reposition(1, 2, w, h - 1) @@ -93,22 +92,21 @@ function multishell.getTabs() return kernel.routines end -function multishell.launch( tProgramEnv, sProgramPath, ... ) +function multishell.launch(env, path, ...) -- backwards compatibility - return multishell.openTab({ - env = tProgramEnv, - path = sProgramPath, + return multishell.openTab(env, { + path = path, args = { ... }, }) end -function multishell.openTab(tab) +function multishell.openTab(env, tab) if not tab.title and tab.path then tab.title = fs.getName(tab.path):match('([^%.]+)') end tab.title = tab.title or 'untitled' tab.window = tab.window or window.create(parentTerm, 1, 2, w, h - 1, false) - tab.onExit = function(self, result, err) + tab.onExit = tab.onExit or function(self, result, err) if not result and err and err ~= 'Terminated' or (err and err ~= 0) then self.terminal.setBackgroundColor(colors.black) if tonumber(err) then @@ -131,14 +129,17 @@ function multishell.openTab(tab) end end - local routine = kernel.run(tab) + local routine, message = kernel.run(env, tab) - if tab.focused then - multishell.setFocus(routine.uid) - else - redrawMenu() + if routine then + if tab.focused then + multishell.setFocus(routine.uid) + else + redrawMenu() + end end - return routine.uid + + return routine and routine.uid, message end function multishell.hideTab(tabId) @@ -316,15 +317,22 @@ kernel.hook('mouse_scroll', function(_, eventData) end) kernel.hook('kernel_ready', function() - overviewId = multishell.openTab({ - path = config.launcher or 'sys/apps/Overview.lua', + overviewId = multishell.openTab(_ENV, { + path = 'sys/apps/shell.lua', + args = { config.launcher or 'sys/apps/Overview.lua' }, isOverview = true, noTerminate = true, focused = true, title = '+', + onExit = function(_, s, m) + if not s then + kernel.halt(s, m) + end + end, }) + multishell.setTitle(overviewId, '+') - multishell.openTab({ + multishell.openTab(_ENV, { path = 'sys/apps/shell.lua', args = { 'sys/apps/autorun.lua' }, title = 'Autorun', diff --git a/sys/kernel.lua b/sys/kernel.lua index 1535b2c..5f86bd4 100644 --- a/sys/kernel.lua +++ b/sys/kernel.lua @@ -108,6 +108,38 @@ function Routine:resume(event, ...) end end +function Routine:run() + self.co = self.co or coroutine.create(function() + local result, err, fn + + if self.fn then + fn = self.fn + _G.setfenv(fn, self.env) + elseif self.path then + fn, err = loadfile(self.path, self.env) + elseif self.chunk then + fn, err = load(self.chunk, self.title, nil, self.env) + end + + if fn then + result, err = trace(fn, table.unpack(self.args or { } )) + else + err = err or 'kernel: invalid routine' + end + + pcall(self.onExit, self, result, err) + self:cleanup() + + if not result then + error(err) + end + end) + + table.insert(kernel.routines, self) + + return self:resume() +end + -- override if any post processing is required function Routine:onExit(status, message) -- self, status, message if not status and message ~= 'Terminated' then @@ -134,9 +166,14 @@ function kernel.getShell() return shell end -kernel.makeEnv = shell.makeEnv +-- each routine inherits the parent's env +function kernel.makeEnv(env) + env = setmetatable(Util.shallowCopy(env or _ENV), { __index = _G }) + _G.requireInjector(env) + return env +end -function kernel.newRoutine(args) +function kernel.newRoutine(env, args) kernel.UID = kernel.UID + 1 local routine = setmetatable({ @@ -147,52 +184,16 @@ function kernel.newRoutine(args) }, { __index = Routine }) Util.merge(routine, args) - routine.env = args.env or shell.makeEnv() + routine.env = args.env or kernel.makeEnv(env) routine.terminal = routine.terminal or routine.window return routine end -local function xprun(env, path, ...) - setmetatable(env, { __index = _G }) - local fn, m = loadfile(path, env) - if fn then - return trace(fn, ...) - end - return fn, m -end - -function kernel.launch(routine) - routine.co = routine.co or coroutine.create(function() - local result, err - - if routine.fn then - result, err = Util.runFunction(routine.env, routine.fn, table.unpack(routine.args or { } )) - elseif routine.path then - result, err = xprun(routine.env, routine.path, table.unpack(routine.args or { } )) - else - err = 'kernel: invalid routine' - end - - pcall(routine.onExit, routine, result, err) - routine:cleanup() - - if not result then - error(err) - end - end) - - table.insert(kernel.routines, routine) - - local s, m = routine:resume() - - return s and routine.uid, m -end - -function kernel.run(args) - local routine = kernel.newRoutine(args) - kernel.launch(routine) - return routine +function kernel.run(env, args) + local routine = kernel.newRoutine(env, args) + local s, m = routine:run() + return s and routine, m end function kernel.raise(uid) @@ -314,9 +315,10 @@ local function init(...) for _,file in ipairs(files) do local level = file:match('(%d).%S+.lua') or 99 if tonumber(level) <= runLevel then + -- All init programs run under the original shell local s, m = shell.run(fs.combine(dir, file)) if not s then - error(m) + error(m, -1) end os.sleep(0) end @@ -331,7 +333,7 @@ local function init(...) shell.run('sys/apps/autorun.lua') local win = window.create(kernel.terminal, 1, 1, w, h, true) - local s, m = kernel.run({ + local s, m = kernel.run(_ENV, { title = args[1], path = 'sys/apps/shell.lua', args = args, @@ -349,7 +351,7 @@ local function init(...) end end -kernel.run({ +kernel.run(_ENV, { fn = init, title = 'init', args = { ... }, diff --git a/sys/modules/opus/crypto/chacha20.lua b/sys/modules/opus/crypto/chacha20.lua index 9db2d08..c98654c 100644 --- a/sys/modules/opus/crypto/chacha20.lua +++ b/sys/modules/opus/crypto/chacha20.lua @@ -100,7 +100,7 @@ local function crypt(data, key, nonce, cntr, round) cntr = tonumber(cntr) or 1 round = tonumber(round) or 20 - local throttle = Util.throttle(function() _syslog('throttle') end) + local throttle = Util.throttle() local out = {} local state = initState(key, nonce, cntr) local blockAmt = math.floor(#data/64) diff --git a/sys/modules/opus/event.lua b/sys/modules/opus/event.lua index 924461e..5560beb 100644 --- a/sys/modules/opus/event.lua +++ b/sys/modules/opus/event.lua @@ -58,6 +58,14 @@ function Routine:resume(event, ...) else s, m = coroutine.resume(self.co, event, ...) end + + if not s and event ~= 'terminate' then + if m and debug and debug.traceback then + local t = (debug.traceback(self.co, 1)) or '' + m = m .. '\n' .. t:match('%d\n(.+)') + end + end + if self:isDead() then self.co = nil self.filter = nil diff --git a/sys/modules/opus/trace.lua b/sys/modules/opus/trace.lua index c8cf2a9..bb12fde 100644 --- a/sys/modules/opus/trace.lua +++ b/sys/modules/opus/trace.lua @@ -32,95 +32,63 @@ local function traceback(x) end end -local function trim_traceback(target, marker) - local ttarget, tmarker = {}, {} - for line in target:gmatch("([^\n]*)\n?") do ttarget[#ttarget + 1] = line end - for line in marker:gmatch("([^\n]*)\n?") do tmarker[#tmarker + 1] = line end +local function trim_traceback(target) + local t = { } + local filters = { + "%[C%]: in function 'xpcall'", + "(...tail calls...)", + "xpcall: $", + "trace.lua:%d+:", + } ---[[ - TODO : fix this trace - Anavrins - if you could take a look, it would be appreciated - -- basically i want the stack logged in a more readable - -- format and the normal code/error message to be returned - - -- unsure why the traceback method concatenates the stack - -- when it can just be returned as a table - would make - -- filtering much simpler - - -- the following seems to reduce the stacktrace way too - -- much - losing most of the relevant call stack - - -- i have modified this a bit from the original - not sure - -- if my changes are causing the issues or not - - -- Trim identical suffixes - local t_len, m_len = #ttarget, #tmarker - while t_len >= 3 and ttarget[t_len] == tmarker[m_len] do - table.remove(ttarget, t_len) - t_len, m_len = t_len - 1, m_len - 1 + local function matchesFilter(line) + for _, filter in pairs(filters) do + if line:match(filter) then + return true + end + end end - -- Trim elements from this file and xpcall invocations - while t_len >= 1 and ttarget[t_len]:find("^\tstack_trace%.lua:%d+:") or - ttarget[t_len] == "\t[C]: in function 'xpcall'" or ttarget[t_len] == " xpcall: " do - table.remove(ttarget, t_len) - t_len = t_len - 1 + for line in target:gmatch("([^\n]*)\n?") do + if not matchesFilter(line) then + table.insert(t, line) + end end -]] - ttarget[#ttarget] = nil -- remove 2 calls added by the added xpcall - ttarget[#ttarget] = nil - return ttarget + return t end ---- Run a function with return function (fn, ...) - -- So this is rather grim: we need to get the full traceback and current one and remove - -- the common prefix - local trace - local args = { ... } - -- xpcall in Lua 5.1 does not accept parameters -- which is not ideal + local args = { ... } local res = table.pack(xpcall(function() return fn(table.unpack(args)) end, traceback)) - if not res[1] then - trace = traceback("trace.lua:1:") - end local ok, err = res[1], res[2] if not ok and err ~= nil then - trace = trim_traceback(err, trace) + local trace = trim_traceback(err) - -- Find the position where the stack traceback actually starts - local trace_starts - for i = #trace, 1, -1 do - if trace[i] == "stack traceback:" then trace_starts = i; break end - end - - _G._syslog('') - for _, line in pairs(trace) do - _G._syslog(line) - end - - -- If this traceback is more than 15 elements long, keep the first 9, last 5 - -- and put an ellipsis between the rest - local max = 10 - if trace_starts and #trace - trace_starts > max then - local keep_starts = trace_starts + 7 - for i = #trace - trace_starts - max, 0, -1 do - table.remove(trace, keep_starts + i) + err = { } + while true do + local line = table.remove(trace, 1) + if not line or line == 'stack traceback:' then + break + end + table.insert(err, line) + end + err = table.concat(err, '\n') + + _G._syslog('\n' .. err .. '\n' .. 'stack traceback:') + for _, v in ipairs(trace) do + if v ~= 'stack traceback:' then + _G._syslog(v:gsub("in function", "in")) end - table.insert(trace, keep_starts, " ...") end - for k, line in pairs(trace) do - trace[k] = line:gsub("in function", " in") - end - - return false, table.remove(trace, 1), table.concat(trace, "\n") + return ok, err end return table.unpack(res, 1, res.n) diff --git a/sys/modules/opus/ui.lua b/sys/modules/opus/ui.lua index fb67603..25c5083 100644 --- a/sys/modules/opus/ui.lua +++ b/sys/modules/opus/ui.lua @@ -117,7 +117,7 @@ function UI:init() if ie.code == 'control-shift-mouse_click' then -- hack local event = currentPage:pointToChild(x, y) - _ENV.multishell.openTab({ + _ENV.multishell.openTab(_ENV, { path = 'sys/apps/Lua.lua', args = { event.element, self, _ENV }, focused = true })