mirror of
https://github.com/20kdc/OC-KittenOS.git
synced 2024-11-11 04:58:07 +11:00
Compare commits
4 Commits
5ac8f9ff11
...
7d1f6d2cae
Author | SHA1 | Date | |
---|---|---|---|
|
7d1f6d2cae | ||
|
a585ce4a75 | ||
|
3ed1cebe25 | ||
|
7c70a1128c |
@ -55,7 +55,7 @@ return {
|
|||||||
},
|
},
|
||||||
["neo-everest"] = {
|
["neo-everest"] = {
|
||||||
desc = "KittenOS NEO / Everest (windowing)",
|
desc = "KittenOS NEO / Everest (windowing)",
|
||||||
v = 5,
|
v = 9,
|
||||||
deps = {
|
deps = {
|
||||||
"neo"
|
"neo"
|
||||||
},
|
},
|
||||||
@ -68,7 +68,7 @@ return {
|
|||||||
},
|
},
|
||||||
["neo-icecap"] = {
|
["neo-icecap"] = {
|
||||||
desc = "KittenOS NEO / Icecap",
|
desc = "KittenOS NEO / Icecap",
|
||||||
v = 8,
|
v = 9,
|
||||||
deps = {
|
deps = {
|
||||||
"neo"
|
"neo"
|
||||||
},
|
},
|
||||||
@ -85,7 +85,7 @@ return {
|
|||||||
},
|
},
|
||||||
["neo-secpolicy"] = {
|
["neo-secpolicy"] = {
|
||||||
desc = "KittenOS NEO / Secpolicy",
|
desc = "KittenOS NEO / Secpolicy",
|
||||||
v = 8,
|
v = 9,
|
||||||
deps = {
|
deps = {
|
||||||
},
|
},
|
||||||
dirs = {
|
dirs = {
|
||||||
|
@ -3,12 +3,16 @@
|
|||||||
|
|
||||||
local _, _, termId = ...
|
local _, _, termId = ...
|
||||||
local ok = pcall(function ()
|
local ok = pcall(function ()
|
||||||
assert(string.sub(termId, 1, 8) == "x.svc.t/")
|
assert(string.sub(termId, 1, 12) == "x.neo.pub.t/")
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
local termClose
|
||||||
|
|
||||||
if not ok then
|
if not ok then
|
||||||
termId = nil
|
termId = nil
|
||||||
neo.executeAsync("svc-t", function (res)
|
neo.executeAsync("svc-t", function (res)
|
||||||
termId = res.access
|
termId = res.access
|
||||||
|
termClose = res.close
|
||||||
neo.scheduleTimer(0)
|
neo.scheduleTimer(0)
|
||||||
end, "luashell")
|
end, "luashell")
|
||||||
while not termId do
|
while not termId do
|
||||||
@ -17,25 +21,105 @@ if not ok then
|
|||||||
end
|
end
|
||||||
TERM = neo.requireAccess(termId, "terminal")
|
TERM = neo.requireAccess(termId, "terminal")
|
||||||
|
|
||||||
TERM.line("KittenOS NEO Lua Shell")
|
-- Using event makes it easier for stuff
|
||||||
|
-- within the shell to not spectacularly explode.
|
||||||
print = function (...)
|
event = require("event")(neo)
|
||||||
local n = {}
|
|
||||||
local s = {...}
|
|
||||||
for i = 1, #s do
|
|
||||||
local v = s[i]
|
|
||||||
if v == nil then
|
|
||||||
v = "nil"
|
|
||||||
end
|
|
||||||
table.insert(n, tostring(v))
|
|
||||||
end
|
|
||||||
TERM.line(table.concat(n, " "))
|
|
||||||
end
|
|
||||||
|
|
||||||
local alive = true
|
local alive = true
|
||||||
|
event.listen("k.procdie", function (_, _, pid)
|
||||||
|
if pid == TERM.pid then
|
||||||
|
alive = false
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
run = function (x, ...)
|
TERM.write(([[
|
||||||
local subPid = neo.executeAsync(x, ...)
|
KittenOS NEO Shell Usage Notes
|
||||||
|
|
||||||
|
Prefixing = is an alias for 'return '.
|
||||||
|
io.read(): Reads a line.
|
||||||
|
print: 'print with table dumping' impl.
|
||||||
|
TERM: Your terminal. (see us-termi doc.)
|
||||||
|
os.execute(): launch terminal apps!
|
||||||
|
tries '*', 'sys-t-*', 'svc-t-*', 'app-*'
|
||||||
|
example: os.execute("luashell")
|
||||||
|
os.exit(): quit the shell
|
||||||
|
=listCmdApps(): -t- (terminal) apps
|
||||||
|
event: useful for setting up listeners
|
||||||
|
without breaking shell functionality
|
||||||
|
]]):gsub("[\r]*\n", "\r\n"))
|
||||||
|
|
||||||
|
function listCmdApps()
|
||||||
|
local apps = {}
|
||||||
|
for _, v in ipairs(neo.listApps()) do
|
||||||
|
if v:sub(4, 6) == "-t-" then
|
||||||
|
table.insert(apps, v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return apps
|
||||||
|
end
|
||||||
|
|
||||||
|
local function vPrint(slike, ...)
|
||||||
|
local s = {...}
|
||||||
|
if #s > 1 then
|
||||||
|
for i = 1, #s do
|
||||||
|
if i ~= 1 then TERM.write("\t") end
|
||||||
|
vPrint(slike, s[i])
|
||||||
|
end
|
||||||
|
elseif slike and type(s[1]) == "string" then
|
||||||
|
TERM.write("\"" .. s[1] .. "\"")
|
||||||
|
elseif type(s[1]) ~= "table" then
|
||||||
|
TERM.write(tostring(s[1]))
|
||||||
|
else
|
||||||
|
TERM.write("{")
|
||||||
|
for k, v in pairs(s[1]) do
|
||||||
|
TERM.write("[")
|
||||||
|
vPrint(true, k)
|
||||||
|
TERM.write("] = ")
|
||||||
|
vPrint(true, v)
|
||||||
|
TERM.write(", ")
|
||||||
|
end
|
||||||
|
TERM.write("}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
print = function (...)
|
||||||
|
vPrint(false, ...)
|
||||||
|
TERM.write("\r\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
local ioBuffer = ""
|
||||||
|
|
||||||
|
io = {
|
||||||
|
read = function ()
|
||||||
|
while alive do
|
||||||
|
local pos = ioBuffer:find("\n")
|
||||||
|
if pos then
|
||||||
|
local line = ioBuffer:sub(1, pos):gsub("\r", "")
|
||||||
|
ioBuffer = ioBuffer:sub(pos + 1)
|
||||||
|
return line
|
||||||
|
end
|
||||||
|
local e = {event.pull()}
|
||||||
|
if e[1] == TERM.id then
|
||||||
|
if e[2] == "data" then
|
||||||
|
ioBuffer = ioBuffer .. e[3]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
write = function (s) TERM.write(s) end
|
||||||
|
}
|
||||||
|
|
||||||
|
local originalOS = os
|
||||||
|
os = setmetatable({}, {
|
||||||
|
__index = originalOS
|
||||||
|
})
|
||||||
|
|
||||||
|
function os.exit()
|
||||||
|
alive = false
|
||||||
|
end
|
||||||
|
|
||||||
|
function os.execute(x, ...)
|
||||||
|
local subPid = neo.executeAsync(x, TERM.id, ...)
|
||||||
if not subPid then
|
if not subPid then
|
||||||
subPid = neo.executeAsync("sys-t-" .. x, TERM.id, ...)
|
subPid = neo.executeAsync("sys-t-" .. x, TERM.id, ...)
|
||||||
end
|
end
|
||||||
@ -49,40 +133,32 @@ run = function (x, ...)
|
|||||||
error("cannot find " .. x)
|
error("cannot find " .. x)
|
||||||
end
|
end
|
||||||
while true do
|
while true do
|
||||||
local e = {coroutine.yield()}
|
local e = {event.pull()}
|
||||||
if e[1] == "k.procdie" then
|
if e[1] == "k.procdie" then
|
||||||
if e[3] == TERM.pid then
|
if e[3] == subPid then
|
||||||
alive = false
|
|
||||||
return
|
|
||||||
elseif e[3] == subPid then
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
exit = function ()
|
|
||||||
alive = false
|
|
||||||
end
|
|
||||||
|
|
||||||
while alive do
|
while alive do
|
||||||
local e = {coroutine.yield()}
|
TERM.write("> ")
|
||||||
if e[1] == "k.procdie" then
|
local code = io.read()
|
||||||
if e[3] == TERM.pid then
|
if code then
|
||||||
alive = false
|
|
||||||
end
|
|
||||||
elseif e[1] == TERM.id then
|
|
||||||
if e[2] == "line" then
|
|
||||||
TERM.line("> " .. e[3])
|
|
||||||
local ok, err = pcall(function ()
|
local ok, err = pcall(function ()
|
||||||
if e[3]:sub(1, 1) == "=" then
|
if code:sub(1, 1) == "=" then
|
||||||
e[3] = "return " .. e[3]:sub(2)
|
code = "return " .. code:sub(2)
|
||||||
end
|
end
|
||||||
print(assert(load(e[3]))())
|
print(assert(load(code))())
|
||||||
end)
|
end)
|
||||||
if not ok then
|
if not ok then
|
||||||
TERM.line(tostring(err))
|
TERM.write(tostring(err) .. "\r\n")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if termClose then
|
||||||
|
termClose()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -15,66 +15,224 @@ local function rW()
|
|||||||
return string.format("%04x", math.random(0, 65535))
|
return string.format("%04x", math.random(0, 65535))
|
||||||
end
|
end
|
||||||
|
|
||||||
local id = "svc.t/" .. rW() .. rW() .. rW() .. rW()
|
local id = "neo.pub.t/" .. rW() .. rW() .. rW() .. rW()
|
||||||
local closeNow = false
|
local closeNow = false
|
||||||
|
|
||||||
|
-- Terminus Registration State --
|
||||||
|
|
||||||
local tReg = neo.requireAccess("r." .. id, "registration")
|
local tReg = neo.requireAccess("r." .. id, "registration")
|
||||||
|
local sendSigs = {}
|
||||||
|
|
||||||
-- unicode.safeTextFormat'd lines
|
-- Display State --
|
||||||
|
-- unicode.safeTextFormat'd lines.
|
||||||
|
-- The size of this must not go below 1.
|
||||||
local console = {}
|
local console = {}
|
||||||
|
-- This must not go below 3.
|
||||||
|
local conW = 40
|
||||||
|
local conCX, conCY = 1, 1
|
||||||
for i = 1, 14 do
|
for i = 1, 14 do
|
||||||
console[i] = (" "):rep(40)
|
console[i] = (" "):rep(conW)
|
||||||
end
|
end
|
||||||
|
|
||||||
local l15 = ""
|
-- Line Editing State --
|
||||||
--++++++++++++++++++++++++++++++++++++++++
|
-- Nil if line editing is off.
|
||||||
|
-- In this case, the console height
|
||||||
-- sW must not go below 3.
|
-- must be adjusted accordingly.
|
||||||
-- sH must not go below 2.
|
local leText = ""
|
||||||
local sW, sH = 40, 15
|
-- These are NOT nil'd out,
|
||||||
local cX = 1
|
-- particularly not the history buffer.
|
||||||
local windows = neo.requireAccess("x.neo.pub.window", "windows")
|
local leCX = 1
|
||||||
local window = windows(sW, sH, title)
|
local leHistory = {
|
||||||
|
-- Size = history buffer size
|
||||||
local function fmtLine(s)
|
"", "", "", ""
|
||||||
s = unicode.safeTextFormat(s)
|
}
|
||||||
local l = unicode.len(s)
|
local function cycleHistoryUp()
|
||||||
return unicode.sub(s .. (" "):rep(sW - l), -sW)
|
local backupFirst = leHistory[1]
|
||||||
|
for i = 1, #leHistory - 1 do
|
||||||
|
leHistory[i] = leHistory[i + 1]
|
||||||
|
end
|
||||||
|
leHistory[#leHistory] = backupFirst
|
||||||
|
end
|
||||||
|
local function cycleHistoryDown()
|
||||||
|
local backup = leHistory[1]
|
||||||
|
for i = 2, #leHistory do
|
||||||
|
backup, leHistory[i] = leHistory[i], backup
|
||||||
|
end
|
||||||
|
leHistory[1] = backup
|
||||||
end
|
end
|
||||||
|
|
||||||
local function line(i)
|
-- Window --
|
||||||
local l, c = console[i] or l15
|
|
||||||
l, c = unicode.safeTextFormat(l, cX)
|
local window = neo.requireAccess("x.neo.pub.window", "window")(conW, #console + 1, title)
|
||||||
l = require("lineedit").draw(sW, l, i == sH and c)
|
|
||||||
if i ~= sH then
|
-- Core Terminal Functions --
|
||||||
window.span(1, i, l, 0xFFFFFF, 0)
|
|
||||||
|
local function setSize(w, h)
|
||||||
|
conW = w
|
||||||
|
while #console < h do
|
||||||
|
table.insert(console, "")
|
||||||
|
end
|
||||||
|
while #console > h do
|
||||||
|
table.remove(console, 1)
|
||||||
|
end
|
||||||
|
for i = 1, #console do
|
||||||
|
console[i] = unicode.sub(console[i], 1, w) .. (" "):rep(w - unicode.len(console[i]))
|
||||||
|
end
|
||||||
|
if leText then
|
||||||
|
window.setSize(w, h + 1)
|
||||||
else
|
else
|
||||||
window.span(1, i, l, 0, 0xFFFFFF)
|
window.setSize(w, h)
|
||||||
|
end
|
||||||
|
conCX, conCY = 1, h
|
||||||
|
end
|
||||||
|
|
||||||
|
local function setLineEditing(state)
|
||||||
|
if state and not leText then
|
||||||
|
leText = ""
|
||||||
|
leCX = 1
|
||||||
|
setSize(conW, #console)
|
||||||
|
elseif leText and not state then
|
||||||
|
leText = nil
|
||||||
|
setSize(conW, #console)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function incoming(s)
|
local function draw(i)
|
||||||
local function shift(f)
|
if console[i] then
|
||||||
|
window.span(1, i, console[i], 0, 0xFFFFFF)
|
||||||
|
elseif leText then
|
||||||
|
window.span(1, i, require("lineedit").draw(conW, unicode.safeTextFormat(leText, leCX)), 0xFFFFFF, 0)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local function drawDisplay()
|
||||||
|
for i = 1, #console do draw(i) end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Terminal Visual --
|
||||||
|
|
||||||
|
local function writeFF()
|
||||||
|
if conCY ~= #console then
|
||||||
|
conCY = conCY + 1
|
||||||
|
else
|
||||||
for i = 1, #console - 1 do
|
for i = 1, #console - 1 do
|
||||||
console[i] = console[i + 1]
|
console[i] = console[i + 1]
|
||||||
end
|
end
|
||||||
console[#console] = f
|
console[#console] = (" "):rep(conW)
|
||||||
end
|
|
||||||
-- Need to break this safely.
|
|
||||||
shift("")
|
|
||||||
for i = 1, unicode.len(s) do
|
|
||||||
local ch = unicode.sub(s, i, i)
|
|
||||||
if unicode.wlen(console[#console] .. ch) > sW then
|
|
||||||
shift(" ")
|
|
||||||
end
|
|
||||||
console[#console] = console[#console] .. ch
|
|
||||||
end
|
|
||||||
for i = 1, #console do
|
|
||||||
line(i)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local sendSigs = {}
|
local function writeData(data)
|
||||||
|
-- handle data until completion
|
||||||
|
while #data > 0 do
|
||||||
|
local char = unicode.sub(data, 1, 1)
|
||||||
|
data = unicode.sub(data, 2)
|
||||||
|
-- handle character
|
||||||
|
if char == "\t" then
|
||||||
|
-- not ideal, but allowed
|
||||||
|
char = " "
|
||||||
|
end
|
||||||
|
if char == "\r" then
|
||||||
|
conCX = 1
|
||||||
|
elseif char == "\n" then
|
||||||
|
conCX = 1
|
||||||
|
writeFF()
|
||||||
|
elseif char == "\a" then
|
||||||
|
-- Bell (er...)
|
||||||
|
elseif char == "\b" then
|
||||||
|
conCX = math.max(1, conCX - 1)
|
||||||
|
elseif char == "\v" or char == "\f" then
|
||||||
|
writeFF()
|
||||||
|
else
|
||||||
|
local charL = unicode.wlen(char)
|
||||||
|
if (conCX + charL - 1) > conW then
|
||||||
|
conCX = 1
|
||||||
|
writeFF()
|
||||||
|
end
|
||||||
|
local spaces = (" "):rep(charL - 1)
|
||||||
|
console[conCY] = unicode.sub(console[conCY], 1, conCX - 1) .. char .. spaces .. unicode.sub(console[conCY], conCX + charL)
|
||||||
|
conCX = conCX + charL
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- The Terminus --
|
||||||
|
|
||||||
|
local tvBuildingCmd = ""
|
||||||
|
local tvBuildingUTF = ""
|
||||||
|
local tvSubnegotiation = false
|
||||||
|
local function incoming(s)
|
||||||
|
tvBuildingCmd = tvBuildingCmd .. s
|
||||||
|
-- Flush Cmd
|
||||||
|
while #tvBuildingCmd > 0 do
|
||||||
|
if tvBuildingCmd:byte() == 255 then
|
||||||
|
-- It's a command. Uhoh.
|
||||||
|
if #tvBuildingCmd < 2 then break end
|
||||||
|
local cmd = tvBuildingCmd:byte(2)
|
||||||
|
local param = tvBuildingCmd:byte(3)
|
||||||
|
local cmdLen = 2
|
||||||
|
-- Command Lengths
|
||||||
|
if cmd >= 251 and cmd <= 254 then cmdLen = 3 end
|
||||||
|
if #tvBuildingCmd < cmdLen then break end
|
||||||
|
if cmd == 240 then
|
||||||
|
-- SE
|
||||||
|
tvSubnegotiation = false
|
||||||
|
elseif cmd == 250 then
|
||||||
|
-- SB
|
||||||
|
tvSubnegotiation = true
|
||||||
|
elseif cmd == 251 and param == 1 then
|
||||||
|
-- WILL ECHO (respond with DO ECHO, disable line editing)
|
||||||
|
-- test using io.write("\xFF\xFB\x01")
|
||||||
|
for _, v in pairs(sendSigs) do
|
||||||
|
v("telnet", "\xFF\xFD\x01")
|
||||||
|
end
|
||||||
|
setLineEditing(false)
|
||||||
|
elseif cmd == 252 and param == 1 then
|
||||||
|
-- WON'T ECHO (respond with DON'T ECHO, enable line editing)
|
||||||
|
for _, v in pairs(sendSigs) do
|
||||||
|
v("telnet", "\xFF\xFE\x01")
|
||||||
|
end
|
||||||
|
setLineEditing(true)
|
||||||
|
elseif cmd == 251 or cmd == 252 then
|
||||||
|
-- WILL/WON'T (x) (respond with DON'T (X))
|
||||||
|
local res = "\xFF\xFE" .. string.char(param)
|
||||||
|
for _, v in pairs(sendSigs) do
|
||||||
|
v("telnet", res)
|
||||||
|
end
|
||||||
|
elseif cmd == 253 or cmd == 254 then
|
||||||
|
-- DO/DON'T (x) (respond with WON'T (X))
|
||||||
|
local res = "\xFF\xFC" .. string.char(param)
|
||||||
|
for _, v in pairs(sendSigs) do
|
||||||
|
v("telnet", res)
|
||||||
|
end
|
||||||
|
elseif cmd == 255 then
|
||||||
|
if not tvSubnegotiation then
|
||||||
|
tvBuildingUTF = tvBuildingUTF .. "\xFF"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
tvBuildingCmd = tvBuildingCmd:sub(cmdLen + 1)
|
||||||
|
else
|
||||||
|
if not tvSubnegotiation then
|
||||||
|
tvBuildingUTF = tvBuildingUTF .. tvBuildingCmd:sub(1, 1)
|
||||||
|
end
|
||||||
|
tvBuildingCmd = tvBuildingCmd:sub(2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- Flush UTF
|
||||||
|
while #tvBuildingUTF > 0 do
|
||||||
|
local head = tvBuildingUTF:byte()
|
||||||
|
local len = 1
|
||||||
|
if head >= 192 and head < 224 then len = 2 end
|
||||||
|
if head >= 224 and head < 240 then len = 3 end
|
||||||
|
if head >= 240 and head < 248 then len = 4 end
|
||||||
|
if head >= 248 and head < 252 then len = 5 end
|
||||||
|
if head >= 252 and head < 254 then len = 6 end
|
||||||
|
if #tvBuildingUTF < len then break end
|
||||||
|
-- verified one full character...
|
||||||
|
local char = tvBuildingUTF:sub(1, len)
|
||||||
|
tvBuildingUTF = tvBuildingUTF:sub(len + 1)
|
||||||
|
writeData(char)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
do
|
do
|
||||||
tReg(function (_, pid, sendSig)
|
tReg(function (_, pid, sendSig)
|
||||||
@ -82,11 +240,12 @@ do
|
|||||||
return {
|
return {
|
||||||
id = "x." .. id,
|
id = "x." .. id,
|
||||||
pid = neo.pid,
|
pid = neo.pid,
|
||||||
line = function (text)
|
write = function (text)
|
||||||
incoming(tostring(text))
|
incoming(tostring(text))
|
||||||
|
drawDisplay()
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
end)
|
end, true)
|
||||||
|
|
||||||
if retTbl then
|
if retTbl then
|
||||||
coroutine.resume(coroutine.create(retTbl), {
|
coroutine.resume(coroutine.create(retTbl), {
|
||||||
@ -99,55 +258,72 @@ do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- This decides the history buffer size.
|
local control = false
|
||||||
local history = {
|
|
||||||
"", "", "", ""
|
|
||||||
}
|
|
||||||
|
|
||||||
local function cycleHistoryUp()
|
|
||||||
local backupFirst = history[1]
|
|
||||||
for i = 1, #history - 1 do
|
|
||||||
history[i] = history[i + 1]
|
|
||||||
end
|
|
||||||
history[#history] = backupFirst
|
|
||||||
end
|
|
||||||
local function cycleHistoryDown()
|
|
||||||
local backup = history[1]
|
|
||||||
for i = 2, #history do
|
|
||||||
backup, history[i] = history[i], backup
|
|
||||||
end
|
|
||||||
history[1] = backup
|
|
||||||
end
|
|
||||||
|
|
||||||
local function key(a, c)
|
local function key(a, c)
|
||||||
if c == 200 then
|
if control then
|
||||||
-- History cursor up (history down)
|
if e[5] == 203 and conW > 8 then
|
||||||
l15 = history[#history]
|
setSize(conW - 1, #console)
|
||||||
cX = 1
|
|
||||||
cycleHistoryDown()
|
|
||||||
return
|
return
|
||||||
elseif c == 208 then
|
elseif e[5] == 200 and #console > 1 then
|
||||||
-- History cursor down (history up)
|
setSize(conW, #console - 1)
|
||||||
l15 = history[#history]
|
return
|
||||||
cX = 1
|
elseif e[5] == 205 then
|
||||||
cycleHistoryUp()
|
setSize(conW + 1, #console)
|
||||||
|
return
|
||||||
|
elseif e[5] == 208 then
|
||||||
|
setSize(conW, #console + 1)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local lT, lC, lX = require("lineedit").key(a, c, l15, cX)
|
end
|
||||||
l15 = lT or l15
|
-- so with the reserved ones dealt with...
|
||||||
cX = lC or cX
|
if not leText then
|
||||||
|
-- Line Editing not active.
|
||||||
|
-- For now support a bare minimum.
|
||||||
|
for _, v in pairs(sendSigs) do
|
||||||
|
if control then
|
||||||
|
if a == 99 then
|
||||||
|
v("telnet", "\xFF\xF4")
|
||||||
|
end
|
||||||
|
elseif a == "\r" then
|
||||||
|
v("data", "\r\n")
|
||||||
|
elseif a then
|
||||||
|
v("data", a)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif not control then
|
||||||
|
-- Line Editing active and control isn't involved
|
||||||
|
if c == 200 or c == 208 then
|
||||||
|
-- History cursor up (history down)
|
||||||
|
leText = leHistory[#leHistory]
|
||||||
|
leCX = unicode.len(leText) + 1
|
||||||
|
if c == 208 then
|
||||||
|
cycleHistoryUp()
|
||||||
|
else
|
||||||
|
cycleHistoryDown()
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local lT, lC, lX = require("lineedit").key(a, c, leText, leCX)
|
||||||
|
leText = lT or leText
|
||||||
|
leCX = lC or leCX
|
||||||
if lX == "nl" then
|
if lX == "nl" then
|
||||||
cycleHistoryUp()
|
cycleHistoryUp()
|
||||||
history[#history] = l15
|
leHistory[#leHistory] = leText
|
||||||
|
-- the whole thing {
|
||||||
|
local fullText = leText .. "\r\n"
|
||||||
|
writeData(fullText)
|
||||||
|
drawDisplay()
|
||||||
for _, v in pairs(sendSigs) do
|
for _, v in pairs(sendSigs) do
|
||||||
v("line", l15)
|
v("data", fullText)
|
||||||
|
end
|
||||||
|
-- }
|
||||||
|
leText = ""
|
||||||
|
leCX = 1
|
||||||
end
|
end
|
||||||
l15 = ""
|
|
||||||
cX = 1
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local control = false
|
|
||||||
while not closeNow do
|
while not closeNow do
|
||||||
local e = {coroutine.yield()}
|
local e = {coroutine.yield()}
|
||||||
if e[1] == "k.procdie" then
|
if e[1] == "k.procdie" then
|
||||||
@ -165,32 +341,16 @@ while not closeNow do
|
|||||||
key(c, 0)
|
key(c, 0)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
line(sH)
|
draw(#console + 1)
|
||||||
elseif e[3] == "key" then
|
elseif e[3] == "key" then
|
||||||
if e[5] == 29 or e[5] == 157 then
|
if e[5] == 29 or e[5] == 157 then
|
||||||
control = e[6]
|
control = e[6]
|
||||||
elseif e[6] then
|
elseif e[6] then
|
||||||
if not control then
|
|
||||||
key(e[4] ~= 0 and unicode.char(e[4]), e[5])
|
key(e[4] ~= 0 and unicode.char(e[4]), e[5])
|
||||||
line(sH)
|
draw(#console + 1)
|
||||||
elseif e[5] == 203 and sW > 8 then
|
|
||||||
sW = sW - 1
|
|
||||||
window.setSize(sW, sH)
|
|
||||||
elseif e[5] == 200 and sH > 2 then
|
|
||||||
sH = sH - 1
|
|
||||||
table.remove(console, 1)
|
|
||||||
window.setSize(sW, sH)
|
|
||||||
elseif e[5] == 205 then
|
|
||||||
sW = sW + 1
|
|
||||||
window.setSize(sW, sH)
|
|
||||||
elseif e[5] == 208 then
|
|
||||||
sH = sH + 1
|
|
||||||
table.insert(console, 1, "")
|
|
||||||
window.setSize(sW, sH)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
elseif e[3] == "line" then
|
elseif e[3] == "line" then
|
||||||
line(e[4])
|
draw(e[4])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -616,7 +616,7 @@ local function key(ku, ka, kc, down)
|
|||||||
elseif kc == 56 then
|
elseif kc == 56 then
|
||||||
isAltDown = down
|
isAltDown = down
|
||||||
end
|
end
|
||||||
if isAltDown and kc == 122 then
|
if isAltDown and ka == 122 then
|
||||||
if focus and down then
|
if focus and down then
|
||||||
local n = table.remove(surfaces, 1)
|
local n = table.remove(surfaces, 1)
|
||||||
table.insert(surfaces, n)
|
table.insert(surfaces, n)
|
||||||
|
@ -81,20 +81,12 @@ local function getPfx(xd, pkg)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local endAcPattern = "/[a-z0-9/%.]*$"
|
local function splitAC(ac)
|
||||||
|
local sb = ac:match("/[a-z0-9/%.]*$")
|
||||||
local function matchesSvc(xd, pkg, perm)
|
if sb then
|
||||||
local pfx = getPfx(xd, pkg)
|
return ac:sub(1, #ac - #sb), sb
|
||||||
if pfx then
|
|
||||||
local permAct = perm
|
|
||||||
local paP = permAct:match(endAcPattern)
|
|
||||||
if paP then
|
|
||||||
permAct = permAct:sub(1, #permAct - #paP)
|
|
||||||
end
|
|
||||||
if permAct == pfx then
|
|
||||||
return "allow"
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
return ac
|
||||||
end
|
end
|
||||||
|
|
||||||
donkonitDFProvider(function (pkg, pid, sendSig)
|
donkonitDFProvider(function (pkg, pid, sendSig)
|
||||||
@ -130,7 +122,8 @@ donkonitDFProvider(function (pkg, pid, sendSig)
|
|||||||
myApi = getPfx("", pkg),
|
myApi = getPfx("", pkg),
|
||||||
lockPerm = function (perm)
|
lockPerm = function (perm)
|
||||||
-- Are we allowed to?
|
-- Are we allowed to?
|
||||||
if not matchesSvc("x.", pkg, perm) then
|
local permPfx, detail = splitAC(perm)
|
||||||
|
if getPfx("x.", pkg) ~= permPfx then
|
||||||
return false, "You don't own this permission."
|
return false, "You don't own this permission."
|
||||||
end
|
end
|
||||||
local set = "perm|*|" .. perm
|
local set = "perm|*|" .. perm
|
||||||
@ -223,7 +216,11 @@ local function secPolicyStage2(pid, proc, perm, req)
|
|||||||
-- Push to ICECAP thread to avoid deadlock b/c wrong event-pull context
|
-- Push to ICECAP thread to avoid deadlock b/c wrong event-pull context
|
||||||
neo.scheduleTimer(0)
|
neo.scheduleTimer(0)
|
||||||
table.insert(todo, function ()
|
table.insert(todo, function ()
|
||||||
local ok, err = pcall(secpol, nexus, settings, proc.pkg, pid, perm, req, matchesSvc)
|
local fPerm = perm
|
||||||
|
if fPerm:sub(1, 2) == "r." then
|
||||||
|
fPerm = splitAC(fPerm)
|
||||||
|
end
|
||||||
|
local ok, err = pcall(secpol, nexus, settings, proc.pkg, pid, fPerm, req, getPfx("", proc.pkg))
|
||||||
if not ok then
|
if not ok then
|
||||||
neo.emergency("Used fallback policy because of run-err: " .. err)
|
neo.emergency("Used fallback policy because of run-err: " .. err)
|
||||||
req(def)
|
req(def)
|
||||||
@ -243,11 +240,7 @@ rootAccess.securityPolicy = function (pid, proc, perm, req)
|
|||||||
end
|
end
|
||||||
-- Do we need to start it?
|
-- Do we need to start it?
|
||||||
if perm:sub(1, 6) == "x.svc." and not neo.usAccessExists(perm) then
|
if perm:sub(1, 6) == "x.svc." and not neo.usAccessExists(perm) then
|
||||||
local appAct = perm:sub(7)
|
local appAct = splitAC(perm:sub(3))
|
||||||
local paP = appAct:match(endAcPattern)
|
|
||||||
if paP then
|
|
||||||
appAct = appAct:sub(1, #appAct - #paP)
|
|
||||||
end
|
|
||||||
-- Prepare for success
|
-- Prepare for success
|
||||||
onReg[perm] = onReg[perm] or {}
|
onReg[perm] = onReg[perm] or {}
|
||||||
local orp = onReg[perm]
|
local orp = onReg[perm]
|
||||||
|
@ -459,7 +459,7 @@ function retrieveAccess(perm, pkg, pid)
|
|||||||
accesses[uid] = function (pkg, pid)
|
accesses[uid] = function (pkg, pid)
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
return function (f)
|
return function (f, secret)
|
||||||
-- Registration function
|
-- Registration function
|
||||||
ensureType(f, "function")
|
ensureType(f, "function")
|
||||||
local accessObjectCache = {}
|
local accessObjectCache = {}
|
||||||
@ -481,8 +481,10 @@ function retrieveAccess(perm, pkg, pid)
|
|||||||
end
|
end
|
||||||
-- returns nil and fails
|
-- returns nil and fails
|
||||||
end
|
end
|
||||||
|
if not secret then
|
||||||
-- Announce registration
|
-- Announce registration
|
||||||
distEvent(nil, "k.registration", uid)
|
distEvent(nil, "k.registration", uid)
|
||||||
|
end
|
||||||
end, function ()
|
end, function ()
|
||||||
-- Registration becomes null (access is held but other processes cannot retrieve object)
|
-- Registration becomes null (access is held but other processes cannot retrieve object)
|
||||||
if accesses[uid] then
|
if accesses[uid] then
|
||||||
|
@ -11,13 +11,19 @@
|
|||||||
-- IRC is usually pretty safe, but no guarantees.
|
-- IRC is usually pretty safe, but no guarantees.
|
||||||
|
|
||||||
-- Returns "allow", "deny", or "ask".
|
-- Returns "allow", "deny", or "ask".
|
||||||
local function actualPolicy(pkg, pid, perm, matchesSvc)
|
local function actualPolicy(pkg, pid, perm, pkgSvcPfx)
|
||||||
-- System stuff is allowed.
|
-- System stuff is allowed.
|
||||||
if pkg:sub(1, 4) == "sys-" then
|
if pkg:sub(1, 4) == "sys-" then
|
||||||
return "allow"
|
return "allow"
|
||||||
end
|
end
|
||||||
|
-- svc-t's job is solely to emulate terminals
|
||||||
|
-- TO INSTALL YOUR OWN TERMINAL EMULATOR:
|
||||||
|
-- perm|app-yourterm|r.neo.t
|
||||||
|
if pkg == "svc-t" and perm == "r.neo.pub.t" then
|
||||||
|
return "allow"
|
||||||
|
end
|
||||||
-- <The following is for apps & services>
|
-- <The following is for apps & services>
|
||||||
-- x.neo.pub (aka Icecap) is open to all
|
-- x.neo.pub.* is open to all
|
||||||
if perm:sub(1, 10) == "x.neo.pub." then
|
if perm:sub(1, 10) == "x.neo.pub." then
|
||||||
return "allow"
|
return "allow"
|
||||||
end
|
end
|
||||||
@ -25,7 +31,8 @@ local function actualPolicy(pkg, pid, perm, matchesSvc)
|
|||||||
if perm == "s.h.component_added" or perm == "s.h.component_removed" or perm == "s.h.tablet_use" or perm == "c.tablet" then
|
if perm == "s.h.component_added" or perm == "s.h.component_removed" or perm == "s.h.tablet_use" or perm == "c.tablet" then
|
||||||
return "allow"
|
return "allow"
|
||||||
end
|
end
|
||||||
if matchesSvc("r.", pkg, perm) then
|
-- Userlevel can register for itself
|
||||||
|
if perm == "r." .. pkgSvcPfx then
|
||||||
return "allow"
|
return "allow"
|
||||||
end
|
end
|
||||||
-- Userlevel has no other registration rights
|
-- Userlevel has no other registration rights
|
||||||
@ -44,8 +51,8 @@ local function actualPolicy(pkg, pid, perm, matchesSvc)
|
|||||||
return "ask"
|
return "ask"
|
||||||
end
|
end
|
||||||
|
|
||||||
return function (nexus, settings, pkg, pid, perm, rsp, matchesSvc)
|
return function (nexus, settings, pkg, pid, perm, rsp, pkgSvcPfx)
|
||||||
local res = actualPolicy(pkg, pid, perm, matchesSvc)
|
local res = actualPolicy(pkg, pid, perm, pkgSvcPfx)
|
||||||
if settings then
|
if settings then
|
||||||
res = settings.getSetting("perm|" .. pkg .. "|" .. perm) or
|
res = settings.getSetting("perm|" .. pkg .. "|" .. perm) or
|
||||||
settings.getSetting("perm|*|" .. perm) or res
|
settings.getSetting("perm|*|" .. perm) or res
|
||||||
|
@ -63,7 +63,8 @@ The security check may be aliased to
|
|||||||
"r.*": Registers a service's API for
|
"r.*": Registers a service's API for
|
||||||
retrieval via the "x." mechanism.
|
retrieval via the "x." mechanism.
|
||||||
Returns a:
|
Returns a:
|
||||||
function (function (pkg, pid, send))
|
function (function (pkg, pid, send),
|
||||||
|
secret)
|
||||||
While the registration is locked on
|
While the registration is locked on
|
||||||
success, attempting to use it will
|
success, attempting to use it will
|
||||||
fail, as no handler has been given.
|
fail, as no handler has been given.
|
||||||
@ -71,6 +72,11 @@ The security check may be aliased to
|
|||||||
registration with a callback used
|
registration with a callback used
|
||||||
for when a process tries to use the
|
for when a process tries to use the
|
||||||
registered API.
|
registered API.
|
||||||
|
Unless 'secret' is truthy, a
|
||||||
|
k.registration event is sent to all
|
||||||
|
processes; using the secret flag is
|
||||||
|
useful for a more ad-hoc security
|
||||||
|
approach.
|
||||||
What that API returns goes to the
|
What that API returns goes to the
|
||||||
target process.
|
target process.
|
||||||
The given "sendSig" function can be
|
The given "sendSig" function can be
|
||||||
|
@ -5,20 +5,34 @@ The "svc-t" program / "x.svc.t"
|
|||||||
--- THEORETICAL TERMINALS MODEL ---
|
--- THEORETICAL TERMINALS MODEL ---
|
||||||
|
|
||||||
The theoretical model for terminals
|
The theoretical model for terminals
|
||||||
in KittenOS NEO is that of a stack
|
in KittenOS NEO is a TELNET client
|
||||||
of processes controlling a player's
|
that only supports the ECHO option,
|
||||||
connection to a MUD, where text is
|
and uses the non-standard behavior
|
||||||
provided to the server and to the
|
of treating ECHO ON as 'enable local
|
||||||
player in a line-by-line format,
|
line editing'.
|
||||||
with no "flow control"/ttyattrs.
|
|
||||||
|
To prevent code size going too far,
|
||||||
|
the client is extremely restricted
|
||||||
|
in capabilities.
|
||||||
|
|
||||||
|
If you really want full support,
|
||||||
|
write a better terminal application.
|
||||||
|
|
||||||
|
Features that get added will be added
|
||||||
|
in accordance with ANSI/TELNET where
|
||||||
|
reasonable or in a compatible-ish
|
||||||
|
fashion where unreasonable.
|
||||||
|
|
||||||
|
The defaults will be set based on
|
||||||
|
whatever app-luashell requires, as
|
||||||
|
this is what is expected of modern
|
||||||
|
terminal systems regardless of what
|
||||||
|
the standards may have to say.
|
||||||
|
|
||||||
A process starting another process
|
A process starting another process
|
||||||
connected to the same terminal is
|
connected to the same terminal is
|
||||||
advised to wait for that process to
|
advised to wait for that process to
|
||||||
die before continuing in terminal
|
die before continuing reading input.
|
||||||
activities, unless some sort of
|
|
||||||
'in-band notification' functionality
|
|
||||||
is intended.
|
|
||||||
|
|
||||||
The controlling process is whichever
|
The controlling process is whichever
|
||||||
process is supposed to be accepting
|
process is supposed to be accepting
|
||||||
@ -32,8 +46,8 @@ The controlling process should show
|
|||||||
acknowledgement that user input has
|
acknowledgement that user input has
|
||||||
been received.
|
been received.
|
||||||
|
|
||||||
User input IS NOT automatically
|
For convenience, terminal echo is on
|
||||||
echoed by the terminal.
|
by default; this is easily remedied.
|
||||||
|
|
||||||
--- ACTUAL USAGE OF TERMINALS ---
|
--- ACTUAL USAGE OF TERMINALS ---
|
||||||
|
|
||||||
@ -42,12 +56,15 @@ Access control on terminals is looser
|
|||||||
to be able to be 'sublet' in some
|
to be able to be 'sublet' in some
|
||||||
cases, including events.
|
cases, including events.
|
||||||
|
|
||||||
|
As such, the secret flag is set for
|
||||||
|
terminal registration.
|
||||||
|
|
||||||
A terminal program is given a string
|
A terminal program is given a string
|
||||||
argument for the ID of the terminal
|
argument for the ID of the terminal
|
||||||
to connect to.
|
to connect to.
|
||||||
|
|
||||||
A terminal always has an ID beginning
|
A terminal always has an ID beginning
|
||||||
with "x.svc.t/". ALWAYS CHECK THIS.
|
with "x.neo.pub.t/". ALWAYS CHECK.
|
||||||
|
|
||||||
Requiring the responsible access
|
Requiring the responsible access
|
||||||
connects to the terminal. All
|
connects to the terminal. All
|
||||||
@ -80,11 +97,22 @@ In either case, when the access has
|
|||||||
|
|
||||||
id = "x.svc.t/<...>"
|
id = "x.svc.t/<...>"
|
||||||
pid = <The terminal's PID.>
|
pid = <The terminal's PID.>
|
||||||
line = function (text): Shows a line.
|
write = function (text): Writes the
|
||||||
|
TELNET data to the terminal.
|
||||||
|
|
||||||
When the user sends a line, an event
|
User input is provided in events:
|
||||||
of: <id>, "line", <text>
|
<id>, "data", <data>
|
||||||
is provided.
|
|
||||||
|
TELNET commands are provided in:
|
||||||
|
<id>, "telnet", <data>
|
||||||
|
|
||||||
|
There is a total of one TELNET
|
||||||
|
command per event, unpadded.
|
||||||
|
|
||||||
|
Notably, intermixing the data part
|
||||||
|
of the data/telnet events in order
|
||||||
|
produces the full terminal-to-server
|
||||||
|
TELNET stream.
|
||||||
|
|
||||||
-- This is released into
|
-- This is released into
|
||||||
the public domain.
|
the public domain.
|
||||||
|
Loading…
Reference in New Issue
Block a user