2018-03-19 10:10:54 +11:00
-- KittenOS N.E.O Kernel: "Tell Mettaton I said hi."
2021-01-12 23:11:00 +11:00
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
2018-03-19 10:10:54 +11:00
2018-03-22 13:33:52 +11:00
-- NOTE: local is considered unnecessary in kernel since 21 March
2018-03-19 10:10:54 +11:00
-- In case of OpenComputers configuration abnormality
2018-03-30 07:06:53 +11:00
readBufSize = 2048
2018-03-19 10:10:54 +11:00
-- A function used for logging, usable by programs.
2018-04-24 05:16:30 +10:00
emergencyFunction = function ( ... )
computer.pushSignal ( " _kosneo_syslog " , " kernel " , ... )
2018-04-24 05:28:43 +10:00
if ocemu and ocemu.log then
2018-04-24 05:16:30 +10:00
pcall ( ocemu.log , ... )
end
end
2018-03-19 10:10:54 +11:00
-- Comment this out if you don't want programs to have
-- access to ocemu's logger.
2018-04-24 05:16:30 +10:00
ocemu = ( component.list ( " ocemu " , true ) ( ) ) or ( component.list ( " sandbox " , true ) ( ) )
2018-03-19 10:10:54 +11:00
if ocemu then
ocemu = component.proxy ( ocemu )
end
2018-04-24 05:16:30 +10:00
-- It is a really bad idea to remove this.
-- If the code inside this block even executes, then removing it is a security risk.
2018-04-07 06:05:47 +10:00
if load ( string.dump ( function ( ) end ) ) then
2018-04-24 05:16:30 +10:00
emergencyFunction ( " detected bytecode access, preventing (only remove this block if you trust every app ever on your KittenOS NEO system) " )
local oldLoad = load
load = function ( c , n , m , ... )
return oldLoad ( c , n , " t " , ... )
end
2018-04-07 06:05:47 +10:00
end
2018-03-19 10:10:54 +11:00
primaryDisk = component.proxy ( computer.getBootAddress ( ) )
2018-03-19 14:08:09 +11:00
-- {{time, func, arg1...}...}
2018-03-19 10:10:54 +11:00
timers = { }
libraries = { }
2018-03-28 00:40:05 +11:00
setmetatable ( libraries , { __mode = " v " } )
2018-03-19 10:10:54 +11:00
-- proc.co = coroutine.create(appfunc)
-- proc.pkg = "pkg"
-- proc.access = {["perm"] = true, ...}
-- proc.denied = {["perm"] = true, ...}
2018-03-19 14:08:09 +11:00
-- proc.deathCBs = {function(), ...}
2018-03-19 10:10:54 +11:00
-- very slightly adjusted total CPU time
-- proc.cpuUsage
processes = { }
-- Maps registration-accesses to function(pkg, pid)
accesses = { }
2018-03-30 07:06:53 +11:00
lastPID = 0
2018-03-19 10:10:54 +11:00
-- Kernel global "idle time" counter, useful for accurate performance data
2018-03-30 07:06:53 +11:00
idleTime = 0
2018-03-19 10:10:54 +11:00
-- This function is critical to wide text support.
function unicode . safeTextFormat ( s , ptr )
local res = " "
if not ptr then ptr = 1 end
local aptr = 1
for i = 1 , unicode.len ( s ) do
local ch = unicode.sub ( s , i , i )
local ex = unicode.charWidth ( ch )
if i < ptr then
aptr = aptr + ex
end
for j = 2 , ex do
ch = ch .. " "
end
res = res .. ch
end
return res , aptr
end
-- The issue with the above function, of course, is that in practice the GPU is a weird mess.
-- So this undoes the above transformation for feeding to gpu.set.
-- (In practice if safeTextFormat supports RTL, and that's a big "if", then this will not undo that.
-- The point is that this converts it into gpu.set format.)
function unicode . undoSafeTextFormat ( s )
local res = " "
local ignoreNext = false
for i = 1 , unicode.len ( s ) do
if not ignoreNext then
local ch = unicode.sub ( s , i , i )
2018-03-22 13:33:52 +11:00
if unicode.charWidth ( ch ) ~= 1 then
if unicode.sub ( s , i + 1 , i + 1 ) ~= " " then
ch = " "
else
ignoreNext = true
end
end
2018-03-19 10:10:54 +11:00
res = res .. ch
else
ignoreNext = false
end
end
return res
end
2018-03-30 07:06:53 +11:00
function loadfile ( s , e )
2018-03-28 00:40:05 +11:00
local h , er = primaryDisk.open ( s )
2018-03-19 10:10:54 +11:00
if h then
local ch = " "
local c = primaryDisk.read ( h , readBufSize )
while c do
ch = ch .. c
c = primaryDisk.read ( h , readBufSize )
end
primaryDisk.close ( h )
return load ( ch , " = " .. s , " t " , e )
end
2018-03-28 00:40:05 +11:00
return nil , tostring ( er )
2018-03-19 10:10:54 +11:00
end
2018-03-30 07:06:53 +11:00
uniqueNEOProtectionObject = { }
2018-03-30 22:36:48 +11:00
wrapMetaCache = { }
setmetatable ( wrapMetaCache , { __mode = " v " } )
2018-03-19 10:10:54 +11:00
function wrapMeta ( t )
if type ( t ) == " table " then
2018-03-30 22:36:48 +11:00
if wrapMetaCache [ t ] then
return wrapMetaCache [ t ]
end
2018-03-19 10:10:54 +11:00
local t2 = { }
2018-03-30 22:36:48 +11:00
wrapMetaCache [ t ] = t2
2018-03-19 10:10:54 +11:00
setmetatable ( t2 , {
__index = function ( a , k ) return wrapMeta ( t [ k ] ) end ,
2018-03-30 11:53:00 +11:00
__newindex = error ,
2018-03-30 22:36:48 +11:00
-- WTF
__call = function ( _ , ... )
return t ( ... )
end ,
2018-04-26 22:50:51 +10:00
__pairs = function ( )
2018-03-19 10:10:54 +11:00
return function ( x , key )
2018-05-30 08:47:20 +10:00
local k , v = next ( t , key )
2018-03-19 10:10:54 +11:00
if k then return k , wrapMeta ( v ) end
2018-03-30 11:53:00 +11:00
end , 9 , nil
2018-03-19 10:10:54 +11:00
end ,
2018-04-26 22:50:51 +10:00
__ipairs = function ( )
2018-03-19 10:10:54 +11:00
return function ( x , key )
key = key + 1
if t [ key ] then
return key , wrapMeta ( t [ key ] )
end
2018-03-30 11:53:00 +11:00
end , 9 , 0
2018-03-19 10:10:54 +11:00
end ,
2018-04-26 22:50:51 +10:00
__len = function ( )
return # t
end ,
2018-03-19 10:10:54 +11:00
__metatable = uniqueNEOProtectionObject
-- Don't protect this table - it'll make things worse
} )
return t2
else
return t
end
end
2018-03-30 07:06:53 +11:00
function ensureType ( a , t )
2018-03-19 10:10:54 +11:00
if type ( a ) ~= t then error ( " Invalid parameter, expected a " .. t ) end
if t == " table " then
if getmetatable ( a ) then error ( " Invalid parameter, has metatable " ) end
end
end
2018-03-30 07:06:53 +11:00
function ensurePathComponent ( s )
2018-05-30 08:47:20 +10:00
if not string.match ( s , " ^[a-zA-Z0-9_%-%+%,%.%#%~%@%'%;%[%]%(%)%&%%%$%! %=%{%}%^ \x80 - \xFF ]+$ " ) then error ( " chars disallowed: " .. s ) end
2018-03-19 10:10:54 +11:00
if s == " . " then error ( " single dot disallowed " ) end
if s == " .. " then error ( " double dot disallowed " ) end
end
2018-03-30 07:06:53 +11:00
function ensurePath ( s , r )
2018-03-30 11:53:00 +11:00
string.gsub ( s , " [^/]+ " , ensurePathComponent )
2018-03-19 10:10:54 +11:00
if s : sub ( 1 , r : len ( ) ) ~= r then error ( " base disallowed " ) end
if s : match ( " // " ) then error ( " // disallowed " ) end
end
-- Use with extreme care.
-- (A process killing itself will actually survive until the next yield... before any of the death events have run.)
2018-03-30 07:06:53 +11:00
function termProc ( pid , reason )
2018-03-19 10:10:54 +11:00
if processes [ pid ] then
-- Immediately prepare for GC, it's possible this is out of memory.
-- If out of memory, then to reduce risk of memory leak by error, memory needs to be freed ASAP.
-- Start by getting rid of all process data.
2018-03-19 14:08:09 +11:00
local dcbs = processes [ pid ] . deathCBs
2018-03-19 10:10:54 +11:00
local pkg = processes [ pid ] . pkg
local usage = processes [ pid ] . cpuUsage
processes [ pid ] = nil
-- This gets rid of a few more bits of data.
for _ , v in ipairs ( dcbs ) do
v ( )
end
-- This finishes off that.
dcbs = nil
2018-03-19 14:08:09 +11:00
if reason then
2018-03-19 10:10:54 +11:00
emergencyFunction ( " d1 " .. pkg .. " / " .. pid )
emergencyFunction ( " d2 " .. reason )
end
-- And this is why it's important, because this generates timers.
-- The important targets of these timers will delete even more data.
distEvent ( nil , " k.procdie " , pkg , pid , reason , usage )
end
end
2018-03-30 07:06:53 +11:00
function execEvent ( k , ... )
2018-03-19 10:10:54 +11:00
if processes [ k ] then
local v = processes [ k ]
local timerA = computer.uptime ( )
local r , reason = coroutine.resume ( v.co , ... )
-- Mostly reliable accounting
v.cpuUsage = v.cpuUsage + ( computer.uptime ( ) - timerA )
2018-03-29 08:15:09 +11:00
reason = ( ( not r ) and tostring ( reason ) ) or nil
local dead = ( not not reason ) or coroutine.status ( v.co ) == " dead "
2018-03-19 10:10:54 +11:00
if dead then
termProc ( k , reason )
2018-03-29 08:15:09 +11:00
return not not reason
2018-03-19 10:10:54 +11:00
end
end
end
function distEvent ( pid , s , ... )
local ev = { ... }
if pid then
local v = processes [ pid ]
if not v then
return
end
2018-03-30 11:53:00 +11:00
if not ( s : sub ( 1 , 2 ) == " k. " or v.access [ " s. " .. s ] or v.access [ " k.root " ] ) then
2018-03-19 10:10:54 +11:00
return
end
2018-03-19 14:08:09 +11:00
-- Schedule the timer to carry the event.
table.insert ( timers , { 0 , execEvent , pid , s , table.unpack ( ev ) } )
else
for k , v in pairs ( processes ) do
distEvent ( k , s , ... )
end
2018-03-19 10:10:54 +11:00
end
end
2018-03-30 07:06:53 +11:00
function lister ( pfx )
return function ( )
local n = primaryDisk.list ( pfx )
local n2 = { }
for k , v in ipairs ( n ) do
if v : sub ( # v - 3 ) == " .lua " then
table.insert ( n2 , v : sub ( 1 , # v - 4 ) )
end
end
return n2
end
end
2018-03-19 10:10:54 +11:00
function loadLibraryInner ( library )
ensureType ( library , " string " )
library = " libs/ " .. library .. " .lua "
ensurePath ( library , " libs/ " )
if libraries [ library ] then return libraries [ library ] end
2018-03-28 00:40:05 +11:00
emergencyFunction ( " loading " .. library )
2018-03-19 10:10:54 +11:00
local l , r = loadfile ( library , baseProcEnv ( ) )
if l then
local ok , al = pcall ( l )
if ok then
2018-04-07 06:05:47 +10:00
al = wrapMeta ( al )
2018-03-19 10:10:54 +11:00
libraries [ library ] = al
return al
else
return nil , al
end
end
return nil , r
end
2018-03-30 11:53:00 +11:00
wrapMath = wrapMeta ( math )
wrapTable = wrapMeta ( table )
wrapString = wrapMeta ( string )
wrapUnicode = wrapMeta ( unicode )
wrapCoroutine = wrapMeta ( coroutine )
2018-04-12 09:04:16 +10:00
-- inject stuff into os
os.totalMemory = computer.totalMemory
os.freeMemory = computer.freeMemory
os.energy = computer.energy
os.maxEnergy = computer.maxEnergy
os.uptime = computer.uptime
os.address = computer.address
wrapOs = wrapMeta ( os )
2018-03-30 11:53:00 +11:00
wrapDebug = wrapMeta ( debug )
2018-04-12 09:04:16 +10:00
wrapBit32 = wrapMeta ( bit32 )
wrapUtf8 = wrapMeta ( utf8 )
2018-03-30 11:53:00 +11:00
baseProcEnvCore = {
_VERSION = _VERSION ,
math = wrapMath ,
table = wrapTable ,
string = wrapString ,
unicode = wrapUnicode ,
coroutine = wrapCoroutine ,
os = wrapOs ,
debug = wrapDebug ,
2018-04-12 09:04:16 +10:00
bit32 = wrapBit32 ,
utf8 = wrapUtf8 ,
2018-03-30 11:53:00 +11:00
require = loadLibraryInner ,
assert = assert , ipairs = ipairs ,
next = function ( t , k )
local mt = getmetatable ( t )
if mt == uniqueNEOProtectionObject then error ( " NEO-Protected Object " ) end
return next ( t , k )
end ,
pairs = pairs , pcall = pcall ,
xpcall = xpcall , select = select ,
type = type , error = error ,
tonumber = tonumber , tostring = tostring ,
setmetatable = setmetatable , getmetatable = function ( n )
local mt = getmetatable ( n )
2018-05-30 08:47:20 +10:00
if rawequal ( mt , uniqueNEOProtectionObject ) then return " NEO-Protected Object " end
2018-03-30 11:53:00 +11:00
return mt
end ,
rawset = function ( t , i , v )
local mt = getmetatable ( t )
2018-05-30 08:47:20 +10:00
if rawequal ( mt , uniqueNEOProtectionObject ) then error ( " NEO-Protected Object " ) end
2018-03-30 11:53:00 +11:00
return rawset ( t , i , v )
end , rawget = rawget , rawlen = rawlen , rawequal = rawequal ,
2018-05-30 08:47:20 +10:00
checkArg = checkArg
2018-03-30 11:53:00 +11:00
}
baseProcNeo = {
emergency = emergencyFunction ,
readBufSize = readBufSize ,
wrapMeta = wrapMeta ,
listProcs = function ( )
local n = { }
for k , v in pairs ( processes ) do
table.insert ( n , { k , v.pkg , v.cpuUsage } )
end
return n
end ,
listApps = lister ( " apps/ " ) ,
listLibs = lister ( " libs/ " ) ,
2018-04-23 00:25:49 +10:00
usAccessExists = function ( accessName )
ensureType ( accessName , " string " )
2018-04-26 04:52:23 +10:00
return not not accesses [ accessName ]
2018-04-23 00:25:49 +10:00
end ,
2018-03-30 11:53:00 +11:00
totalIdleTime = function ( ) return idleTime end ,
ensurePath = ensurePath ,
ensurePathComponent = ensurePathComponent ,
ensureType = ensureType
}
baseProcEnvMT = {
__index = baseProcEnvCore ,
__metatable = uniqueNEOProtectionObject
}
baseProcNeoMT = {
__index = baseProcNeo ,
__metatable = uniqueNEOProtectionObject
}
function baseProcEnv ( )
local pe = setmetatable ( { } , baseProcEnvMT )
2018-05-30 08:47:20 +10:00
pe.load = function ( a , b , c , d , ... )
if rawequal ( d , nil ) then
d = pe
end
return load ( a , b , c , d , ... )
end
2018-03-30 11:53:00 +11:00
pe.neo = setmetatable ( { } , baseProcNeoMT )
pe._G = pe
return pe
end
2018-03-19 14:08:09 +11:00
-- These two are hooks for k.root level applications to change policy.
-- Only a k.root application is allowed to do this for obvious reasons.
2018-03-28 00:40:05 +11:00
function securityPolicy ( pid , proc , perm , req )
2018-03-21 01:18:59 +11:00
-- Important safety measure : only sys-* gets anything at first
2018-03-28 00:40:05 +11:00
req ( proc.pkg : sub ( 1 , 4 ) == " sys- " )
2018-03-19 14:08:09 +11:00
end
function runProgramPolicy ( ipkg , pkg , pid , ... )
-- VERY specific injunction here:
-- non "sys-" apps NEVER start "sys-" apps
-- This is part of the "default security policy" below:
-- sys- has all access
-- anything else has none
if ipkg : sub ( 1 , 4 ) == " sys- " then
if pkg : sub ( 1 , 4 ) ~= " sys- " then
return nil , " non-sys app trying to start sys app "
end
end
2018-03-29 08:15:09 +11:00
return true
2018-03-19 14:08:09 +11:00
end
2018-03-19 10:10:54 +11:00
function retrieveAccess ( perm , pkg , pid )
-- Return the access lib and the death callback.
-- Access categories are sorted into:
-- "c.<hw>": Component
-- "s.<event>": Signal receiver (with responsibilities for Security Request watchers)
-- "s.k.<...>": Kernel stuff
2018-04-12 09:04:16 +10:00
-- "s.k.procnew" : New process (pkg, pid, ppkg, ppid)
2018-03-19 14:08:09 +11:00
-- "s.k.procdie" : Process dead (pkg, pid, reason, usage)
-- "s.k.registration" : Registration of service alert ("x." .. etc)
-- "s.k.deregistration" : Registration of service alert ("x." .. etc)
-- "s.k.securityresponse" : Response from security policy (accessId, accessObj)
2018-03-19 10:10:54 +11:00
-- "s.h.<...>": Incoming HW messages
-- "s.x.<endpoint>": This access is actually useless on it's own - it is given by x.<endpoint>
-- "k.<x>": Kernel
2018-03-19 14:08:09 +11:00
-- "k.root": _ENV (holy grail), and indirectly security request control (which is basically equivalent to this)
2018-03-19 10:10:54 +11:00
-- "k.computer": computer
-- "r.<endpoint>": Registration Of Service...
-- "x.<endpoint>": Access Of Service (handled by r. & accesses table)
if accesses [ perm ] then
return accesses [ perm ] ( pkg , pid )
end
if perm == " k.root " then
return _ENV
end
if perm == " k.computer " then
return wrapMeta ( computer )
end
if perm == " k.kill " then
return function ( npid )
ensureType ( npid , " number " )
termProc ( npid , " Killed by " .. pkg .. " / " .. pid )
end
end
if perm : sub ( 1 , 2 ) == " s. " then
-- This is more of a "return success". Signal access is determined by the access/denied maps.
return true
end
if perm : sub ( 1 , 2 ) == " c. " then
-- Allows for simple "Control any of these connected to the system" APIs,
-- for things the OS shouldn't be poking it's nose in.
local primary = nil
local temporary = nil
local t = perm : sub ( 3 )
if t == " filesystem " then
2018-03-30 22:36:48 +11:00
primary = wrapMeta ( primaryDisk )
temporary = wrapMeta ( component.proxy ( computer.tmpAddress ( ) ) )
2018-03-19 10:10:54 +11:00
end
return {
list = function ( )
local i = component.list ( t , true )
return function ( )
local ii = i ( )
if not ii then return nil end
2018-03-30 22:36:48 +11:00
return wrapMeta ( component.proxy ( ii ) )
2018-03-19 10:10:54 +11:00
end
end ,
primary = primary ,
temporary = temporary
}
end
if perm : sub ( 1 , 2 ) == " r. " then
local uid = " x " .. perm : sub ( 2 )
local sid = " s.x " .. perm : sub ( 2 )
if accesses [ uid ] then return nil end
accesses [ uid ] = function ( pkg , pid )
return nil
end
2020-04-02 09:21:36 +11:00
return function ( f , secret )
2018-03-19 10:10:54 +11:00
-- Registration function
ensureType ( f , " function " )
local accessObjectCache = { }
accesses [ uid ] = function ( pkg , pid )
-- Basically, a per registration per process cache.
-- This is a consistent yet flexible behavior.
if accessObjectCache [ pid ] then
return accessObjectCache [ pid ]
end
processes [ pid ] . access [ sid ] = true
local ok , a = pcall ( f , pkg , pid , function ( ... )
distEvent ( pid , uid , ... )
end )
if ok then
accessObjectCache [ pid ] = a
return a , function ( )
accessObjectCache [ pid ] = nil
end
end
-- returns nil and fails
end
2020-04-02 09:21:36 +11:00
if not secret then
-- Announce registration
distEvent ( nil , " k.registration " , uid )
end
2018-03-19 10:10:54 +11:00
end , function ( )
-- Registration becomes null (access is held but other processes cannot retrieve object)
if accesses [ uid ] then
distEvent ( nil , " k.deregistration " , uid )
end
accesses [ uid ] = nil
end
end
end
2018-04-12 09:04:16 +10:00
function start ( pkg , ppkg , ppid , ... )
2018-03-19 10:10:54 +11:00
local proc = { }
local pid = lastPID
2018-05-30 08:47:20 +10:00
emergencyFunction ( " starting: " , pkg )
2018-03-19 10:10:54 +11:00
lastPID = lastPID + 1
local function startFromUser ( ipkg , ... )
2018-03-29 08:15:09 +11:00
ensureType ( ipkg , " string " )
2018-04-07 06:05:47 +10:00
local ok , n = pcall ( ensurePathComponent , ipkg .. " .lua " )
if not ok then return nil , n end
2018-03-29 08:15:09 +11:00
local k , r = runProgramPolicy ( ipkg , pkg , pid , ... )
if k then
return start ( ipkg , pkg , pid , ... )
else
return k , r
end
2018-03-19 10:10:54 +11:00
end
local function osExecuteCore ( handler , ... )
local pid , err = startFromUser ( ... )
while pid do
local sig = { coroutine.yield ( ) }
2018-03-29 08:15:09 +11:00
handler ( table.unpack ( sig ) )
2018-03-19 10:10:54 +11:00
if sig [ 1 ] == " k.procdie " then
if sig [ 3 ] == pid then
return 0 , sig [ 4 ]
end
end
end
return - 1 , err
end
local requestAccessAsync = function ( perm )
ensureType ( perm , " string " )
-- Safety-checked, prepare security event.
2018-03-28 00:40:05 +11:00
local req = function ( res )
2018-03-19 10:10:54 +11:00
if processes [ pid ] then
local n = nil
local n2 = nil
2018-03-28 00:40:05 +11:00
if res then
2018-03-19 10:10:54 +11:00
proc.access [ perm ] = true
2018-03-19 14:08:09 +11:00
proc.denied [ perm ] = nil
2018-03-19 10:10:54 +11:00
n , n2 = retrieveAccess ( perm , pkg , pid )
if n2 then
2018-03-19 14:08:09 +11:00
table.insert ( processes [ pid ] . deathCBs , n2 )
2018-03-19 10:10:54 +11:00
end
else
proc.denied [ perm ] = true
end
distEvent ( pid , " k.securityresponse " , perm , n )
end
end
2018-03-19 14:08:09 +11:00
-- outer security policy:
2018-03-19 10:10:54 +11:00
if proc.access [ " k.root " ] or proc.access [ perm ] or proc.denied [ perm ] then
-- Use cached result to prevent possible unintentional security service spam
2018-03-28 00:40:05 +11:00
req ( proc.access [ " k.root " ] or not proc.denied [ perm ] )
2018-03-19 10:10:54 +11:00
return
end
2018-03-19 14:08:09 +11:00
-- Denied goes to on to prevent spam
proc.denied [ perm ] = true
2018-03-28 00:40:05 +11:00
securityPolicy ( pid , proc , perm , req )
2018-03-19 10:10:54 +11:00
end
local env = baseProcEnv ( )
env.neo . pid = pid
2018-04-09 09:04:40 +10:00
env.neo . pkg = pkg
2018-03-19 10:10:54 +11:00
env.neo . executeAsync = startFromUser
env.neo . execute = function ( ... )
return osExecuteCore ( function ( ) end , ... )
end
env.neo . executeExt = osExecuteCore
env.neo . requestAccessAsync = requestAccessAsync
env.neo . requestAccess = function ( perm , handler )
requestAccessAsync ( perm )
2018-04-04 11:48:10 +10:00
handler = handler or function ( ) end
2018-03-19 10:10:54 +11:00
while true do
local n = { coroutine.yield ( ) }
2018-03-29 08:15:09 +11:00
handler ( table.unpack ( n ) )
2018-03-19 10:10:54 +11:00
if n [ 1 ] == " k.securityresponse " then
-- Security response - if it involves the permission, then take it
if n [ 2 ] == perm then return n [ 3 ] end
end
end
end
2018-03-19 14:08:09 +11:00
env.neo . requireAccess = function ( perm , reason )
-- Allows for hooking
local res = env.neo . requestAccess ( perm )
if not res then error ( pkg .. " needed " .. perm .. " for " .. ( reason or " some reason " ) ) end
return res
end
2018-03-19 10:10:54 +11:00
env.neo . scheduleTimer = function ( time )
ensureType ( time , " number " )
local tag = { }
2018-04-12 09:04:16 +10:00
table.insert ( timers , { time , execEvent , pid , " k.timer " , tag , time } )
2018-03-19 10:10:54 +11:00
return tag
end
local appfunc , r = loadfile ( " apps/ " .. pkg .. " .lua " , env )
if not appfunc then
return nil , r
end
2018-03-29 08:15:09 +11:00
proc.co = coroutine.create ( function ( ... ) local r = { xpcall ( appfunc , debug.traceback , ... ) } if not r [ 1 ] then error ( table.unpack ( r , 2 ) ) end return table.unpack ( r , 2 ) end )
2018-03-19 10:10:54 +11:00
proc.pkg = pkg
2018-03-30 11:53:00 +11:00
proc.access = { }
2018-03-19 10:10:54 +11:00
proc.denied = { }
2018-03-19 14:08:09 +11:00
-- You are dead. Not big surprise.
proc.deathCBs = { function ( ) pcall ( function ( ) env.neo . dead = true end ) end }
2018-03-19 10:10:54 +11:00
proc.cpuUsage = 0
2018-03-19 14:08:09 +11:00
-- Note the target process doesn't get the procnew (the dist occurs before it's creation)
2018-04-12 09:04:16 +10:00
pcall ( distEvent , nil , " k.procnew " , pkg , pid , ppkg , ppid )
2018-03-19 10:10:54 +11:00
processes [ pid ] = proc
2018-04-12 09:04:16 +10:00
table.insert ( timers , { 0 , execEvent , pid , ppkg , ppid , ... } )
2018-03-19 10:10:54 +11:00
return pid
end
2018-03-19 14:08:09 +11:00
-- Kernel Scheduling Loop --
2018-03-19 10:10:54 +11:00
if not start ( " sys-init " ) then error ( " Could not start sys-init " ) end
2018-03-19 14:08:09 +11:00
2018-03-19 10:10:54 +11:00
while true do
2018-03-30 08:03:52 +11:00
local tmr = nil
for i = 1 , 16 do
tmr = nil
local now = computer.uptime ( )
2018-03-30 11:53:00 +11:00
local didAnything = false -- for early exit
2018-03-30 08:03:52 +11:00
local k = 1
while timers [ k ] do
local v = timers [ k ]
if v [ 1 ] <= now then
table.remove ( timers , k )
if v [ 2 ] ( table.unpack ( v , 3 ) ) then
2018-03-30 11:53:00 +11:00
didAnything = false -- to break
2018-03-30 08:03:52 +11:00
tmr = 0.05
break
end
didAnything = true
else
if not tmr then
tmr = v [ 1 ]
2018-03-19 10:10:54 +11:00
else
2018-03-30 08:03:52 +11:00
tmr = math.min ( tmr , v [ 1 ] )
2018-03-19 10:10:54 +11:00
end
2018-03-30 08:03:52 +11:00
k = k + 1
2018-03-19 10:10:54 +11:00
end
end
2018-03-30 08:03:52 +11:00
if not didAnything then break end
end
now = computer.uptime ( ) -- the above probably took a while
local dist = tmr and math.max ( 0.05 , tmr - now )
local signal = { computer.pullSignal ( dist ) }
idleTime = idleTime + ( computer.uptime ( ) - now )
if signal [ 1 ] then
distEvent ( nil , " h. " .. signal [ 1 ] , select ( 2 , table.unpack ( signal ) ) )
end
2018-03-19 10:10:54 +11:00
end