mirror of
https://github.com/Ale32bit/Capy64.git
synced 2025-01-18 10:36:44 +00:00
Isolated shell environments
This commit is contained in:
parent
2bf4b17577
commit
f5d6bbfaae
6 changed files with 197 additions and 51 deletions
|
@ -1,4 +1,4 @@
|
|||
local version = "0.1.0"
|
||||
local version = "0.1.1"
|
||||
local systemDirectory = "/sys"
|
||||
|
||||
print("Starting CapyOS")
|
||||
|
|
|
@ -4,6 +4,7 @@ local fs = require("fs")
|
|||
local machine = require("machine")
|
||||
local argparser = require("argparser")
|
||||
local scheduler = require("scheduler")
|
||||
local createPackageEnvironment = require("shell.package")
|
||||
|
||||
local exit = false
|
||||
local parentShell = shell
|
||||
|
@ -16,15 +17,19 @@ shell.aliases = parentShell and parentShell.aliases or {}
|
|||
|
||||
local currentDir = parentShell and parentShell.getDir() or shell.homePath
|
||||
|
||||
local function buildEnvironment(path, args, argf)
|
||||
local function buildEnvironment(command, filepath, args, argf)
|
||||
local arg = { table.unpack(args, 2) }
|
||||
arg[0] = path
|
||||
arg[0] = command
|
||||
arg.string = argf
|
||||
|
||||
local envPackage = createPackageEnvironment(filepath)
|
||||
envPackage.loaded.scheduler = scheduler
|
||||
|
||||
return setmetatable({
|
||||
shell = shell,
|
||||
arg = arg,
|
||||
}, { __index = _G })
|
||||
scheduler = scheduler,
|
||||
}, { __index = envPackage.loaded._G })
|
||||
end
|
||||
|
||||
function shell.getDir()
|
||||
|
@ -78,7 +83,7 @@ function shell.run(...)
|
|||
end
|
||||
end
|
||||
|
||||
local env = buildEnvironment(command, args, argf)
|
||||
local env = buildEnvironment(command, path, args, argf)
|
||||
|
||||
local func, err = loadfile(path, "t", env)
|
||||
|
||||
|
@ -90,11 +95,13 @@ function shell.run(...)
|
|||
local ok, err
|
||||
local function run()
|
||||
ok, err = pcall(func, table.unpack(args, 2))
|
||||
|
||||
end
|
||||
|
||||
local programTask = scheduler.spawn(run)
|
||||
local programTask, yielded = scheduler.spawn(run)
|
||||
|
||||
if yielded then
|
||||
coroutine.yield("scheduler_task_end")
|
||||
end
|
||||
|
||||
if not ok then
|
||||
io.stderr.print(err)
|
||||
|
|
|
@ -55,39 +55,6 @@ local function findParent()
|
|||
return nil
|
||||
end
|
||||
|
||||
function scheduler.spawn(func, options)
|
||||
expect(1, func, "function")
|
||||
expect(2, options, "nil", "table")
|
||||
|
||||
options = options or {}
|
||||
options.args = options.args or {}
|
||||
|
||||
local source = debug.getinfo(2)
|
||||
|
||||
local task = newTask()
|
||||
local pid = #tasks + 1
|
||||
task.pid = pid
|
||||
task.options = options
|
||||
task.source = source.source
|
||||
task.uuid = tostring(func)
|
||||
task.thread = coroutine.create(func)
|
||||
task.started = false
|
||||
local parent = findParent()
|
||||
if parent then
|
||||
task.parent = parent.pid
|
||||
table.insert(parent.children, pid)
|
||||
end
|
||||
task.filters = {}
|
||||
task.children = {}
|
||||
task.eventQueue = {}
|
||||
|
||||
tasks[pid] = task
|
||||
|
||||
processes = processes + 1
|
||||
|
||||
return task
|
||||
end
|
||||
|
||||
local function cascadeKill(pid, err)
|
||||
local task = tasks[pid]
|
||||
if not task then
|
||||
|
@ -116,6 +83,50 @@ local function cascadeKill(pid, err)
|
|||
end
|
||||
end
|
||||
|
||||
local function resumeTask(task, yieldPars)
|
||||
local pars = table.pack(coroutine.resume(task.thread, table.unpack(yieldPars)))
|
||||
if pars[1] then
|
||||
task.filters = table.pack(table.unpack(pars, 2))
|
||||
return coroutine.status(task.thread) ~= "dead"
|
||||
else
|
||||
cascadeKill(task.pid, pars[2])
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
function scheduler.spawn(func, options)
|
||||
expect(1, func, "function")
|
||||
expect(2, options, "nil", "table")
|
||||
|
||||
options = options or {}
|
||||
options.args = options.args or {}
|
||||
|
||||
local source = debug.getinfo(2)
|
||||
|
||||
local task = newTask()
|
||||
local pid = #tasks + 1
|
||||
task.pid = pid
|
||||
task.options = options
|
||||
task.source = source.source
|
||||
task.uuid = tostring(func)
|
||||
task.thread = coroutine.create(func)
|
||||
local parent = findParent()
|
||||
if parent then
|
||||
task.parent = parent.pid
|
||||
table.insert(parent.children, pid)
|
||||
end
|
||||
task.filters = {}
|
||||
task.children = {}
|
||||
task.eventQueue = {}
|
||||
task.skip = true
|
||||
|
||||
tasks[pid] = task
|
||||
|
||||
processes = processes + 1
|
||||
|
||||
return task, resumeTask(task, task.options.args)
|
||||
end
|
||||
|
||||
function scheduler.kill(pid)
|
||||
expect(1, pid, "number")
|
||||
cascadeKill(pid)
|
||||
|
@ -146,15 +157,10 @@ function scheduler.init()
|
|||
yieldPars = table.pack(table.unpack(ev, 3))
|
||||
end
|
||||
if yieldPars[1] ~= "scheduler" and not task.filters or #task.filters == 0 or contains(task.filters, yieldPars[1]) or yieldPars[1] == "interrupt" then
|
||||
if not task.started then
|
||||
yieldPars = task.options.args
|
||||
task.started = true
|
||||
end
|
||||
local pars = table.pack(coroutine.resume(task.thread, table.unpack(yieldPars)))
|
||||
if pars[1] then
|
||||
task.filters = table.pack(table.unpack(pars, 2))
|
||||
if task.skip then
|
||||
task.skip = false
|
||||
else
|
||||
cascadeKill(pid, pars[2])
|
||||
resumeTask(task, yieldPars)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
112
Capy64/Assets/Lua/CapyOS/sys/lib/shell/package.lua
Normal file
112
Capy64/Assets/Lua/CapyOS/sys/lib/shell/package.lua
Normal file
|
@ -0,0 +1,112 @@
|
|||
local expect = require("expect").expect
|
||||
local fs = require("fs")
|
||||
local nativePackage = package
|
||||
|
||||
local function copyTable(source, target)
|
||||
target = target or {}
|
||||
|
||||
for k, v in pairs(source) do
|
||||
target[k] = v
|
||||
end
|
||||
|
||||
return target
|
||||
end
|
||||
|
||||
local hostPackages = copyTable(nativePackage._host)
|
||||
|
||||
local function createPreloadSearcher(envPackage)
|
||||
return function(name)
|
||||
if not envPackage.preload[name] then
|
||||
return string.format("no field package.preload['%s']", name)
|
||||
end
|
||||
return envPackage.preload[name], ":preload:"
|
||||
end
|
||||
end
|
||||
|
||||
local function createLoaderSearcher(envPackage)
|
||||
return function(name)
|
||||
local path, err = envPackage.searchpath(name, envPackage.path)
|
||||
|
||||
if not path then
|
||||
return err
|
||||
end
|
||||
|
||||
local func, err = loadfile(path)
|
||||
if not func then
|
||||
return string.format("error loading module '%s' from file '%s':\t%s", name, path, err)
|
||||
end
|
||||
|
||||
return func, path
|
||||
end
|
||||
end
|
||||
|
||||
local function createEnvironment(filePath)
|
||||
local envPackage = {
|
||||
cpath = nativePackage.cpath,
|
||||
searchpath = nativePackage.searchpath,
|
||||
config = nativePackage.config,
|
||||
searchers = {},
|
||||
loaded = {},
|
||||
preload = {},
|
||||
}
|
||||
|
||||
local dirName = fs.getDir(filePath)
|
||||
--envPackage.path = string.format("%s/?.lua;%s/?/init.lua;", dirName, dirName) .. nativePackage.path
|
||||
envPackage.path = nativePackage.path
|
||||
|
||||
envPackage.searchers[1] = createPreloadSearcher(envPackage)
|
||||
envPackage.searchers[2] = createLoaderSearcher(envPackage)
|
||||
|
||||
local function envRequire(modname)
|
||||
expect(1, modname, "string", "number")
|
||||
modname = tostring(modname)
|
||||
|
||||
if envPackage.loaded[modname] then
|
||||
return envPackage.loaded[modname]
|
||||
end
|
||||
|
||||
local errorOutput = ""
|
||||
local libFunction, libPath
|
||||
for i = 1, #envPackage.searchers do
|
||||
local par, path = envPackage.searchers[i](modname)
|
||||
if type(par) == "function" then
|
||||
libFunction, libPath = par, path
|
||||
break
|
||||
else
|
||||
errorOutput = errorOutput .. "\n\t" .. par
|
||||
end
|
||||
end
|
||||
|
||||
if not libFunction then
|
||||
error(string.format("module '%s' not found:%s", modname, errorOutput), 2)
|
||||
end
|
||||
|
||||
local ok, par = pcall(libFunction)
|
||||
if not ok then
|
||||
error(par, 0)
|
||||
end
|
||||
|
||||
if par == nil then
|
||||
envPackage.loaded[modname] = true
|
||||
return true
|
||||
end
|
||||
|
||||
envPackage.loaded[modname] = par
|
||||
|
||||
return par, libPath
|
||||
end
|
||||
|
||||
copyTable(hostPackages, envPackage.loaded)
|
||||
envPackage.loaded.package = envPackage
|
||||
|
||||
local env_G = copyTable(envPackage.loaded._G or _G)
|
||||
envPackage.loaded._G = env_G
|
||||
env_G._G = env_G
|
||||
|
||||
envPackage.loaded._G.package = envPackage
|
||||
envPackage.loaded._G.require = envRequire
|
||||
|
||||
return envPackage, envRequire
|
||||
end
|
||||
|
||||
return createEnvironment
|
|
@ -177,7 +177,7 @@ local function bootScreen()
|
|||
term.setPos(1,2)
|
||||
writeCenter("Capy64")
|
||||
term.setPos(1,4)
|
||||
writeCenter("Powered by Capybaras")
|
||||
writeCenter("(c) 2023 AlexDevs")
|
||||
|
||||
term.setPos(1, h - 1)
|
||||
writeCenter("Press F2 to open setup")
|
||||
|
|
|
@ -136,6 +136,7 @@ internal class RuntimeManager : IComponent
|
|||
luaState = new LuaState();
|
||||
_game.LuaRuntime = luaState;
|
||||
luaState.Init();
|
||||
CopyHostLibraries();
|
||||
|
||||
emitter = new(_game.EventEmitter, luaState);
|
||||
|
||||
|
@ -162,11 +163,31 @@ internal class RuntimeManager : IComponent
|
|||
}
|
||||
}
|
||||
|
||||
private void CopyHostLibraries()
|
||||
{
|
||||
// table that will contain host libraries
|
||||
luaState.Thread.NewTable();
|
||||
|
||||
luaState.Thread.GetGlobal("package");
|
||||
luaState.Thread.GetField(-1, "loaded");
|
||||
|
||||
luaState.Thread.PushNil();
|
||||
while (luaState.Thread.Next(-2))
|
||||
{
|
||||
var libname = luaState.Thread.ToString(-2);
|
||||
luaState.Thread.SetField(1, libname);
|
||||
}
|
||||
|
||||
luaState.Thread.Rotate(1, -1);
|
||||
luaState.Thread.SetField(1, "_host");
|
||||
luaState.Thread.SetTop(0);
|
||||
}
|
||||
|
||||
private void LoadFirmware()
|
||||
{
|
||||
var firmwareContent = File.ReadAllText(Path.Combine(Capy64.AssetsPath, "Lua/firmware.lua"));
|
||||
var errored = luaState.Thread.DoString(firmwareContent);
|
||||
if(errored)
|
||||
if (errored)
|
||||
{
|
||||
throw new LuaException(luaState.Thread.ToString(-1));
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue