mirror of
https://github.com/Ale32bit/Capy64.git
synced 2025-01-18 18:46:43 +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"
|
local systemDirectory = "/sys"
|
||||||
|
|
||||||
print("Starting CapyOS")
|
print("Starting CapyOS")
|
||||||
|
|
|
@ -4,6 +4,7 @@ local fs = require("fs")
|
||||||
local machine = require("machine")
|
local machine = require("machine")
|
||||||
local argparser = require("argparser")
|
local argparser = require("argparser")
|
||||||
local scheduler = require("scheduler")
|
local scheduler = require("scheduler")
|
||||||
|
local createPackageEnvironment = require("shell.package")
|
||||||
|
|
||||||
local exit = false
|
local exit = false
|
||||||
local parentShell = shell
|
local parentShell = shell
|
||||||
|
@ -16,15 +17,19 @@ shell.aliases = parentShell and parentShell.aliases or {}
|
||||||
|
|
||||||
local currentDir = parentShell and parentShell.getDir() or shell.homePath
|
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) }
|
local arg = { table.unpack(args, 2) }
|
||||||
arg[0] = path
|
arg[0] = command
|
||||||
arg.string = argf
|
arg.string = argf
|
||||||
|
|
||||||
|
local envPackage = createPackageEnvironment(filepath)
|
||||||
|
envPackage.loaded.scheduler = scheduler
|
||||||
|
|
||||||
return setmetatable({
|
return setmetatable({
|
||||||
shell = shell,
|
shell = shell,
|
||||||
arg = arg,
|
arg = arg,
|
||||||
}, { __index = _G })
|
scheduler = scheduler,
|
||||||
|
}, { __index = envPackage.loaded._G })
|
||||||
end
|
end
|
||||||
|
|
||||||
function shell.getDir()
|
function shell.getDir()
|
||||||
|
@ -78,7 +83,7 @@ function shell.run(...)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local env = buildEnvironment(command, args, argf)
|
local env = buildEnvironment(command, path, args, argf)
|
||||||
|
|
||||||
local func, err = loadfile(path, "t", env)
|
local func, err = loadfile(path, "t", env)
|
||||||
|
|
||||||
|
@ -90,11 +95,13 @@ function shell.run(...)
|
||||||
local ok, err
|
local ok, err
|
||||||
local function run()
|
local function run()
|
||||||
ok, err = pcall(func, table.unpack(args, 2))
|
ok, err = pcall(func, table.unpack(args, 2))
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local programTask = scheduler.spawn(run)
|
local programTask, yielded = scheduler.spawn(run)
|
||||||
|
|
||||||
|
if yielded then
|
||||||
coroutine.yield("scheduler_task_end")
|
coroutine.yield("scheduler_task_end")
|
||||||
|
end
|
||||||
|
|
||||||
if not ok then
|
if not ok then
|
||||||
io.stderr.print(err)
|
io.stderr.print(err)
|
||||||
|
|
|
@ -55,39 +55,6 @@ local function findParent()
|
||||||
return nil
|
return nil
|
||||||
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)
|
|
||||||
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 function cascadeKill(pid, err)
|
||||||
local task = tasks[pid]
|
local task = tasks[pid]
|
||||||
if not task then
|
if not task then
|
||||||
|
@ -116,6 +83,50 @@ local function cascadeKill(pid, err)
|
||||||
end
|
end
|
||||||
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)
|
function scheduler.kill(pid)
|
||||||
expect(1, pid, "number")
|
expect(1, pid, "number")
|
||||||
cascadeKill(pid)
|
cascadeKill(pid)
|
||||||
|
@ -146,15 +157,10 @@ function scheduler.init()
|
||||||
yieldPars = table.pack(table.unpack(ev, 3))
|
yieldPars = table.pack(table.unpack(ev, 3))
|
||||||
end
|
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 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
|
if task.skip then
|
||||||
yieldPars = task.options.args
|
task.skip = false
|
||||||
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))
|
|
||||||
else
|
else
|
||||||
cascadeKill(pid, pars[2])
|
resumeTask(task, yieldPars)
|
||||||
end
|
end
|
||||||
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)
|
term.setPos(1,2)
|
||||||
writeCenter("Capy64")
|
writeCenter("Capy64")
|
||||||
term.setPos(1,4)
|
term.setPos(1,4)
|
||||||
writeCenter("Powered by Capybaras")
|
writeCenter("(c) 2023 AlexDevs")
|
||||||
|
|
||||||
term.setPos(1, h - 1)
|
term.setPos(1, h - 1)
|
||||||
writeCenter("Press F2 to open setup")
|
writeCenter("Press F2 to open setup")
|
||||||
|
|
|
@ -136,6 +136,7 @@ internal class RuntimeManager : IComponent
|
||||||
luaState = new LuaState();
|
luaState = new LuaState();
|
||||||
_game.LuaRuntime = luaState;
|
_game.LuaRuntime = luaState;
|
||||||
luaState.Init();
|
luaState.Init();
|
||||||
|
CopyHostLibraries();
|
||||||
|
|
||||||
emitter = new(_game.EventEmitter, luaState);
|
emitter = new(_game.EventEmitter, luaState);
|
||||||
|
|
||||||
|
@ -162,6 +163,26 @@ 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()
|
private void LoadFirmware()
|
||||||
{
|
{
|
||||||
var firmwareContent = File.ReadAllText(Path.Combine(Capy64.AssetsPath, "Lua/firmware.lua"));
|
var firmwareContent = File.ReadAllText(Path.Combine(Capy64.AssetsPath, "Lua/firmware.lua"));
|
||||||
|
|
Loading…
Reference in a new issue