mirror of
https://github.com/20kdc/OC-KittenOS.git
synced 2024-11-16 23:48:05 +11:00
7bde8fee55
Since this is after the technical "release", version numbers have been bumped to 1. Changes before this commit for R1: Kernel memory usage reduction schemes, with some security fixes. Still need to deal w/ proxies (see later) Changes in this commit: Some various little things in apps CLAW inet actually works now on 192K sys-icecap no longer uses the event/neoux combination, and now handles Everest disappearance as a mass-close, but still handles Everest not being around on window create. So it still handles every situation that matters. neoux no longer handles everest crash protection. Security policy and filedialog obviously don't use neoux anymore. Kernel now only guarantees parsing, not event-loop, by executeAsync This is safer and allows app-launcher to get rid of NeoUX by any means necessary. wrapMeta cache now exists, and proxies get wrapMeta'd to deal with various low-priority security shenanigans. This is a *stopgap* until I work out how to force OCEmu to give me totally accurate boot-time memory figures, so I can create the ultimate lowmem proxy. I'm calling it "puppet". FG knows why.
236 lines
6.9 KiB
Lua
236 lines
6.9 KiB
Lua
-- This is released into the public domain.
|
|
-- No warranty is provided, implied or otherwise.
|
|
|
|
-- s-icecap : Responsible for x.neo.pub API, crash dialogs, and security policy that isn't "sys- has ALL access, anything else has none"
|
|
-- In general, this is what userspace will be interacting with in some way or another to get stuff done
|
|
|
|
local settings = neo.requireAccess("x.neo.sys.manage", "security sysconf access")
|
|
|
|
local fs = neo.requireAccess("c.filesystem", "file managers")
|
|
|
|
local donkonitDFProvider = neo.requireAccess("r.neo.pub.base", "creating basic NEO APIs")
|
|
|
|
local targsDH = {} -- data disposal
|
|
|
|
local todo = {}
|
|
|
|
local onEverest = {}
|
|
local everestWindows = {}
|
|
|
|
local nexus
|
|
|
|
local function resumeWF(...)
|
|
local ok, e = coroutine.resume(...)
|
|
if not ok then
|
|
e = tostring(e)
|
|
neo.emergency(e)
|
|
nexus.startDialog(e, "ice")
|
|
end
|
|
return ok
|
|
end
|
|
|
|
nexus = {
|
|
createNexusThread = function (f, ...)
|
|
local t = coroutine.create(f)
|
|
if not resumeWF(t, ...) then return end
|
|
local early = neo.requestAccess("x.neo.pub.window")
|
|
if early then
|
|
onEverest[#onEverest] = nil
|
|
resumeWF(t, early)
|
|
end
|
|
return function ()
|
|
for k, v in ipairs(onEverest) do
|
|
if v == t then
|
|
table.remove(onEverest, k)
|
|
return
|
|
end
|
|
end
|
|
end
|
|
end,
|
|
create = function (w, h, t)
|
|
local thr = coroutine.running()
|
|
table.insert(onEverest, thr)
|
|
local everest = coroutine.yield()
|
|
local dw = everest(w, h, title)
|
|
everestWindows[dw.id] = thr
|
|
return dw
|
|
end,
|
|
startDialog = function (tx, ti)
|
|
local fmt = require("fmttext")
|
|
local txl = fmt.fmtText(unicode.safeTextFormat(tx), 40)
|
|
fmt = nil
|
|
nexus.createNexusThread(function ()
|
|
local w = nexus.create(40, #txl, ti)
|
|
while true do
|
|
local ev, a = coroutine.yield()
|
|
if ev == "line" then
|
|
w.span(1, a, txl[a], 0xFFFFFF, 0)
|
|
elseif ev == "close" then
|
|
w.close()
|
|
return
|
|
end
|
|
end
|
|
end)
|
|
end,
|
|
close = function (wnd)
|
|
wnd.close()
|
|
everestWindows[wnd.id] = nil
|
|
end
|
|
}
|
|
|
|
donkonitDFProvider(function (pkg, pid, sendSig)
|
|
local prefixNS = "data/" .. pkg
|
|
local prefixWS = "data/" .. pkg .. "/"
|
|
fs.primary.makeDirectory(prefixNS)
|
|
local openHandles = {}
|
|
targsDH[pid] = function ()
|
|
for k, v in pairs(openHandles) do
|
|
v()
|
|
end
|
|
end
|
|
return {
|
|
showFileDialogAsync = function (forWrite)
|
|
-- Not hooked into the event API, so can't safely interfere
|
|
-- Thus, this is async and uses a return event.
|
|
local tag = {}
|
|
neo.scheduleTimer(0)
|
|
table.insert(todo, function ()
|
|
-- sys-filedialog is yet another "library to control memory usage".
|
|
local closer = require("sys-filedialog")(event, nexus, function (res) openHandles[tag] = nil sendSig("filedialog", tag, res) end, fs, pkg, forWrite)
|
|
openHandles[tag] = closer
|
|
end)
|
|
return tag
|
|
end,
|
|
-- Paths must begin with / implicitly
|
|
list = function (path)
|
|
if type(path) ~= "string" then error("Expected path to be string") end
|
|
path = prefixNS .. path
|
|
neo.ensurePath(path, prefixWS)
|
|
if path:sub(#path, #path) ~= "/" then error("Expected / at end") end
|
|
return fs.primary.list(path:sub(1, #path - 1))
|
|
end,
|
|
makeDirectory = function (path)
|
|
if type(path) ~= "string" then error("Expected path to be string") end
|
|
path = prefixNS .. path
|
|
neo.ensurePath(path, prefixWS)
|
|
if path:sub(#path, #path) == "/" then error("Expected no / at end") end
|
|
return fs.primary.makeDirectory(path)
|
|
end,
|
|
rename = function (path1, path2)
|
|
if type(path1) ~= "string" then error("Expected path to be string") end
|
|
if type(path2) ~= "string" then error("Expected path to be string") end
|
|
path1 = prefixNS .. path1
|
|
path2 = prefixNS .. path2
|
|
neo.ensurePath(path1, prefixWS)
|
|
neo.ensurePath(path2, prefixWS)
|
|
if path:sub(#path1, #path1) == "/" then error("Expected no / at end") end
|
|
if path:sub(#path2, #path2) == "/" then error("Expected no / at end") end
|
|
return fs.primary.rename(path1, path2)
|
|
end,
|
|
open = function (path, mode)
|
|
if type(path) ~= "string" then error("Expected path to be string") end
|
|
if type(mode) ~= "boolean" then error("Expected mode to be boolean (writing)") end
|
|
path = prefixNS .. path
|
|
neo.ensurePath(path, prefixWS)
|
|
if path:sub(#path, #path) == "/" then error("Expected no / at end") end
|
|
local fw, closer = require("sys-filewrap")(fs.primary, path, mode)
|
|
local oc = fw.close
|
|
fw.close = function ()
|
|
oc()
|
|
openHandles[fw] = nil
|
|
end
|
|
openHandles[fw] = closer
|
|
return fw
|
|
end,
|
|
remove = function (path)
|
|
if type(path) ~= "string" then error("Expected path to be string") end
|
|
path = prefixNS .. path
|
|
neo.ensurePath(path, prefixWS)
|
|
if path:sub(#path, #path) == "/" then error("Expected no / at end") end
|
|
return fs.primary.remove(path)
|
|
end,
|
|
stat = function (path)
|
|
if type(path) ~= "string" then error("Expected path to be string") end
|
|
path = prefixNS .. path
|
|
neo.ensurePath(path, prefixWS)
|
|
if path:sub(#path, #path) == "/" then error("Expected no / at end") end
|
|
if not fs.primary.exists(path) then return nil end
|
|
return {
|
|
fs.primary.isDirectory(path),
|
|
fs.primary.size(path),
|
|
fs.primary.lastModified(path)
|
|
}
|
|
end,
|
|
-- getLabel/setLabel have nothing to do with this
|
|
spaceUsed = fs.primary.spaceUsed,
|
|
spaceTotal = fs.primary.spaceTotal,
|
|
isReadOnly = fs.primary.isReadOnly
|
|
}
|
|
end)
|
|
|
|
-- Connect in security policy now
|
|
local rootAccess = neo.requireAccess("k.root", "installing GUI integration")
|
|
local backup = rootAccess.securityPolicyINIT or rootAccess.securityPolicy
|
|
rootAccess.securityPolicyINIT = backup
|
|
rootAccess.securityPolicy = function (pid, proc, perm, req)
|
|
if neo.dead then
|
|
return backup(pid, proc, perm, req)
|
|
end
|
|
local def = proc.pkg:sub(1, 4) == "sys-"
|
|
local secpol, err = require("sys-secpolicy")
|
|
if not secpol then
|
|
-- Failsafe.
|
|
neo.emergency("Used fallback policy because of load-err: " .. err)
|
|
req(def)
|
|
return
|
|
end
|
|
-- Push to ICECAP thread to avoid deadlock b/c wrong event-pull context
|
|
neo.scheduleTimer(0)
|
|
table.insert(todo, function ()
|
|
local ok, err = pcall(secpol, nexus, settings, proc.pkg, pid, perm, req)
|
|
if not ok then
|
|
neo.emergency("Used fallback policy because of run-err: " .. err)
|
|
req(def)
|
|
end
|
|
end)
|
|
end
|
|
|
|
while true do
|
|
local ev = {coroutine.yield()}
|
|
if ev[1] == "k.procdie" then
|
|
local _, pkg, pid, reason = table.unpack(ev)
|
|
if targsDH[pid] then
|
|
targsDH[pid]()
|
|
end
|
|
targsDH[pid] = nil
|
|
if reason then
|
|
nexus.startDialog(string.format("%s/%i died:\n%s", pkg, pid, reason), "error")
|
|
end
|
|
elseif ev[1] == "k.timer" then
|
|
local nt = todo
|
|
todo = {}
|
|
for _, v in ipairs(nt) do
|
|
local ok, e = pcall(v)
|
|
if not ok then
|
|
nexus.startDialog(tostring(e), "terr")
|
|
end
|
|
end
|
|
elseif ev[1] == "k.registration" then
|
|
if ev[2] == "x.neo.pub.window" then
|
|
local nt = onEverest
|
|
onEverest = {}
|
|
for _, v in ipairs(nt) do
|
|
coroutine.resume(v, neo.requestAccess("x.neo.pub.window"))
|
|
end
|
|
end
|
|
elseif ev[1] == "x.neo.pub.window" then
|
|
local v = everestWindows[ev[2]]
|
|
if v then
|
|
resumeWF(v, table.unpack(ev, 3))
|
|
if coroutine.status(v) == "dead" then
|
|
everestWindows[ev[2]] = nil
|
|
end
|
|
end
|
|
end
|
|
end
|