Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --[[
- Baseboard by MudkipTheEpic
- Meant to be used as a startup file for OSes, or just standalone.
- Provides "base" services such as:
- -"raw" multitasking
- -filesystem sandboxing
- -suggest more, and maybe they will be added :D
- ]]
- if base --[[and base.override]] then
- if fs.exists("start") then
- return dofile("start")
- end
- return
- end
- _G.base=_G.base or {}
- --Let's start with the override..
- --Probably not the BEST way to do this, but it's good enough for government work..
- --[[if not _G.base.override then --Not overriden yet
- os.startTimer(0)
- coroutine.yield() --Fix the os.queueEvent bug
- os.queueEvent("rednet_message",0,0,"dns") --Crashes rednet
- local _printError = printError
- local _osrun=os.run
- os.run = function() os.run=_osrun end
- _G.printError = function() _G.printError = _printError _G.base.override = true return dofile(programName) end --Take control
- return --Time to override
- end
- os.unloadAPI("rednet")
- os.loadAPI("rednet")]]
- --Helper Functions:
- local function call(f,...) --Pass errors and return values back
- local tResults = {pcall(f,...)}
- if tResults[1] then
- return select(2,unpack(tResults))
- else
- error(tResults[2] or "Error",3)
- end
- end
- local function assert( condition, message, level ) --theoriginalbit's custom assert (thanks)
- level = tonumber( level ) or 1
- level = level == 0 and 0 or (level + 1)
- if not condition then
- error( message or "assertion failed!", level )
- end
- return condition
- end
- local function invertTable(t)
- local newT={}
- for k,v in pairs(t) do
- newT[v]=k
- end
- return newT
- end
- local function copyTable(t)
- local newT={}
- for k,v in pairs(t) do
- newT[k]=v
- end
- return newT
- end
- --Sandboxing Module:
- function base.getSandboxedFs(sandboxPath)
- local fs = copyTable(fs)
- local function resolve(_file) --Returns the true absolute path
- assert(type(_file)=="string","Expected string",2)
- local newPath = fs.combine("/",_file).."/"
- if newPath == ".." or newPath:sub(1,3) == "../" then
- error("Invalid Path",2)
- end
- return newPath
- end
- local sandboxPath = call(resolve,sandboxPath)
- local function isInDisk(path) --Is the path a disk or inside of a disk
- local tPer = peripheral.getNames()
- return fs.getDrive(path) and invertTable(tPer)[fs.getDrive(path)] and fs.getDrive(path)
- end
- function getSandboxed(_path) --Get the real version
- local path = call(resolve,_path)
- local drive = fs.getDrive(path)
- if drive and drive == "rom" or isInDisk(path) then
- return path
- end
- return fs.combine(sandboxPath,path)
- end
- local function patchSimple(fsFunc,countPaths)
- return function(...)
- local tArgs = {...}
- local newArgs = {}
- local tError = {}
- for i=1,countPaths do
- table.insert(tError,"string")
- end
- local errorMessage = "Expected "..table.concat(tError,", ")
- for i=1,countPaths do
- assert(type(tArgs[i])=="string",errorMessage,2)
- newArgs[i]=call(getSandboxed,tArgs[i])
- end
- return call(fsFunc,unpack(newArgs))
- end
- end
- local newFs={} --Start patching
- newFs.exists = patchSimple(fs.exists,1)
- newFs.isDir = patchSimple(fs.isDir,1)
- newFs.getName = fs.getName
- newFs.makeDir = patchSimple(fs.makeDir,1)
- newFs.getSize = patchSimple(fs.getSize,1)
- newFs.copy = patchSimple(fs.copy,2)
- newFs.combine = fs.combine
- newFs.getFreeSpace = patchSimple(fs.getFreeSpace,1)
- newFs.isReadOnly = fs.isReadOnly
- newFs.getDir = fs.getDir
- newFs.getDrive = fs.getDrive
- function newFs.move(_from,_to)
- local from = call(getSandboxed,_from)
- local to = call(getSandboxed,_to)
- if fs.isReadOnly(call(resolve,_from)) then
- error("Access denied",2)
- end
- return call(fs.move,from,to)
- end
- function newFs.delete(_path)
- local path = call(getSandboxed,_path)
- if fs.isReadOnly(call(resolve,_path)) then
- error("Access denied",2)
- end
- end
- function newFs.open(_path,mode)
- local path = call(getSandboxed,_path)
- return call(fs.open,path,mode)
- end
- function newFs.list(_path)
- local path = call(resolve,_path)
- if path == "/" then
- local tList=fs.list(sandboxPath)
- for k,v in pairs(fs.list"/") do
- if fs.isReadOnly(v) then
- table.insert(tList,v)
- end
- end
- return tList
- end
- return fs.list(call(getSandboxed,path))
- end
- function newFs.find(wildcard)
- assert(type(wildcard)=="string","Expected string",2)
- local tRet = fs.find("/"..fs.combine(sandboxPath,wildcard))
- for k,v in pairs(tRet) do
- if v:sub(1,#sandboxPath)==sandboxPath then
- tRet[k]=v:sub(#sandboxPath+1,#v)
- end
- end
- for k,v in pairs(fs.find(wildcard)) do
- if fs.isReadOnly(v) then
- table.insert(tRet,v)
- end
- end
- return tRet
- end
- return newFs
- end
- --
- --
- --
- --Multitasking Module:
- local function readyToYield(pool)
- for i=1,table.maxn(pool) do
- if pool[i] and #pool[i].events>0 then return false end
- end
- return true
- end
- local function clean(pool)
- for i=1,table.maxn(pool) do
- if pool[i] and pool[i].status=="dead" then
- pool[i]=nil
- end
- end
- end
- local function startTimer(manager,nCo,time)
- local tId=os.startTimer(time)
- manager.timers[tId]=nCo
- return tId
- end
- local function createCoroutine(_co,...)
- local tEvents={{...}}
- local tFilter=nil
- local isPaused=false
- local hasRun=false
- local manager
- if getfenv(_co).os then
- getfenv(_co).os.startTimer=function(t) assert(type(t)=="number","Expected number",2) return manager and startTimer(manager,manager.running,t) end
- end
- local co=coroutine.create(_co)
- local resume=function(self,...)
- if self.status=="dead" then return "dead" end
- local tArgs={...}
- if table.maxn(tArgs)>0 and not isPaused then
- table.insert(tEvents,tArgs)
- end
- if table.maxn(tEvents)>0 then
- if not isPaused then
- local event=table.remove(tEvents,1) or {}
- if (tFilter==nil) or (tFilter==event[1]) or event[1]=="terminate" then
- hasRun=true
- tFilter=select(2,coroutine.resume(co,unpack(event))) --Spent day debugging, forgot the first return value o coroutine.resume is the success...
- end
- end
- end
- if self.status=="dead" then return "dead" end
- end
- return setmetatable({
- resume=function(self,...)
- return resume(self,...)
- end,
- passEvent=function(self,...)
- if not isPaused then
- table.insert(tEvents,{...})
- end
- end,
- kill=function(self)
- rawset(self,"status","dead")
- end,
- },{
- __index=function(self,k)
- if k=="status" then
- return coroutine.status(co)
- elseif k=="tEvents" or k=="events" then
- return tEvents
- elseif k=="isPaused" then
- return isPaused
- elseif k=="hasRun" then
- return hasRun
- end
- end,
- __newindex=function(self,k,v)
- if k=="isPaused" and type(v)=="boolean" then
- isPaused=v
- end
- if k=="manager" then
- manager = v
- end
- end,
- __call=function(self,...)
- return resume(self,...)
- end
- })
- end
- local tManageTemplate = {
- run=function(self,...)
- local tPool=self["tPool"]
- if table.maxn(tPool)==0 then return false end
- for i=1,table.maxn(tPool) do
- if tPool[i] and not tPool[i].hasRun then
- tPool[i]()
- end
- end
- local firstRun=true
- local tEvents=#{...}>0 and {...} or {coroutine.yield()}
- if tEvents[1]=="timer" and tEvents[2] and self.timers[tEvents[2]] then
- table.insert(self[self.timers[tEvents[2]]].events,{"timer",self})
- firstRun=false
- end
- repeat
- for i=1,table.maxn(tPool) do
- if (tPool[i] and tPool[i].status~="dead") and (firstRun) then
- tPool[i]:passEvent(unpack(tEvents))
- end
- if tPool[i] and tPool[i].status~="dead" and #tPool[i].events>0 then
- rawset(self,running,i)
- if tPool[i]:resume() == "dead" then
- tPool[i]=nil
- end
- rawset(self,running,nil)
- end
- end
- firstRun=false
- until readyToYield(tPool)
- clean(tPool)
- return table.maxn(tPool)>0
- end,
- run_forever=function(self)
- if not self:run() then return false end
- while self:run() do end
- return true
- end,
- addCo=function(self,co,...)
- self.tPool[table.maxn(self.tPool)+1]=createCoroutine(co,...)
- return table.maxn(self.tPool)
- end,
- timers={},
- getNumForCo=function(self,co)
- for i=1,table.maxn(self) do
- if self.tPool[i]==co then return i end
- end
- end,
- }
- local function newManager(self,main,...)
- local tResults={pcall( function(main,...)
- main=main or function() pcall(loadfile("rom/programs/shell")) end
- local tPool={createCoroutine(main,...)}
- local tEvents={}
- local tManager={}
- setmetatable(tManager,{
- __index=function(t,k)
- if k=="tPool" or k=="pool" then
- return tPool
- elseif k=="tEvents" or k=="events" then
- return tEvents
- elseif tonumber(k) then
- k=tonumber(k)
- return tPool[k]
- else
- return tManageTemplate[k]
- end
- end,
- __call=tManageTemplate["run"],
- })
- return tManager
- end, main,...)}
- if tResults[1] then
- return select(2,unpack(tResults))
- else
- return false,"Could not create a new manager!"
- end
- end
- --Start multitasking
- shell.exit()
- base.manager = newManager()
- base.delay = function(time,f,...)
- return base.manager:addCo(function() sleep(time) return call(f,...) end))
- end
- base.manager:run_forever() --Houston, we have liftoff.
Advertisement
Add Comment
Please, Sign In to add comment