MudkipTheEpic

Baseboard

Nov 15th, 2014
263
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 9.14 KB | None | 0 0
  1. --[[
  2. Baseboard by MudkipTheEpic
  3.  
  4. Meant to be used as a startup file for OSes, or just standalone.
  5. Provides "base" services such as:
  6.     -"raw" multitasking
  7.     -filesystem sandboxing
  8.     -suggest more, and maybe they will be added :D
  9. ]]
  10.  
  11. if base --[[and base.override]] then
  12.     if fs.exists("start") then
  13.         return dofile("start")
  14.     end
  15.     return
  16. end
  17.  
  18. _G.base=_G.base or {}
  19.  
  20. --Let's start with the override..
  21. --Probably not the BEST way to do this, but it's good enough for government work..
  22. --[[if not _G.base.override then --Not overriden yet
  23.     os.startTimer(0)
  24.     coroutine.yield() --Fix the os.queueEvent bug
  25.     os.queueEvent("rednet_message",0,0,"dns") --Crashes rednet
  26.     local _printError = printError
  27.     local _osrun=os.run
  28.     os.run = function() os.run=_osrun end
  29.     _G.printError = function() _G.printError = _printError _G.base.override = true return dofile(programName) end --Take control
  30.     return --Time to override
  31. end
  32. os.unloadAPI("rednet")
  33. os.loadAPI("rednet")]]
  34.  
  35.  
  36.  
  37. --Helper Functions:
  38. local function call(f,...) --Pass errors and return values back
  39.     local tResults = {pcall(f,...)}
  40.     if tResults[1] then
  41.         return select(2,unpack(tResults))
  42.     else
  43.         error(tResults[2] or "Error",3)
  44.     end
  45. end
  46.  
  47. local function assert( condition, message, level ) --theoriginalbit's custom assert (thanks)
  48.   level = tonumber( level ) or 1
  49.   level = level == 0 and 0 or (level + 1)
  50.   if not condition then
  51.     error( message or "assertion failed!", level )
  52.   end
  53.   return condition
  54. end
  55.  
  56. local function invertTable(t)
  57.     local newT={}
  58.     for k,v in pairs(t) do
  59.         newT[v]=k
  60.     end
  61.     return newT
  62. end
  63.  
  64. local function copyTable(t)
  65.     local newT={}
  66.     for k,v in pairs(t) do
  67.         newT[k]=v
  68.     end
  69.     return newT
  70. end
  71.  
  72. --Sandboxing Module:
  73.  
  74.  
  75.  
  76. function base.getSandboxedFs(sandboxPath)
  77.     local fs = copyTable(fs)
  78.     local function resolve(_file) --Returns the true absolute path
  79.         assert(type(_file)=="string","Expected string",2)
  80.         local newPath = fs.combine("/",_file).."/"
  81.         if newPath == ".." or newPath:sub(1,3) == "../" then
  82.             error("Invalid Path",2)
  83.         end
  84.         return newPath
  85.     end
  86.     local sandboxPath = call(resolve,sandboxPath)
  87.  
  88.     local function isInDisk(path) --Is the path a disk or inside of a disk
  89.         local tPer = peripheral.getNames()
  90.         return fs.getDrive(path) and invertTable(tPer)[fs.getDrive(path)] and fs.getDrive(path)
  91.     end
  92.  
  93.     function getSandboxed(_path) --Get the real version
  94.         local path = call(resolve,_path)
  95.         local drive = fs.getDrive(path)
  96.         if drive and drive == "rom" or isInDisk(path) then
  97.             return path
  98.         end
  99.         return fs.combine(sandboxPath,path)
  100.     end
  101.  
  102.     local function patchSimple(fsFunc,countPaths)
  103.         return function(...)
  104.             local tArgs = {...}
  105.             local newArgs = {}
  106.             local tError = {}
  107.             for i=1,countPaths do
  108.                 table.insert(tError,"string")
  109.             end
  110.             local errorMessage = "Expected "..table.concat(tError,", ")
  111.             for i=1,countPaths do
  112.                 assert(type(tArgs[i])=="string",errorMessage,2)
  113.                 newArgs[i]=call(getSandboxed,tArgs[i])
  114.             end
  115.             return call(fsFunc,unpack(newArgs))
  116.         end
  117.     end
  118.  
  119.     local newFs={} --Start patching
  120.  
  121.     newFs.exists = patchSimple(fs.exists,1)
  122.     newFs.isDir = patchSimple(fs.isDir,1)
  123.     newFs.getName = fs.getName
  124.     newFs.makeDir = patchSimple(fs.makeDir,1)
  125.     newFs.getSize = patchSimple(fs.getSize,1)
  126.     newFs.copy = patchSimple(fs.copy,2)
  127.     newFs.combine = fs.combine
  128.     newFs.getFreeSpace = patchSimple(fs.getFreeSpace,1)
  129.     newFs.isReadOnly = fs.isReadOnly
  130.     newFs.getDir = fs.getDir
  131.     newFs.getDrive = fs.getDrive
  132.  
  133.     function newFs.move(_from,_to)
  134.         local from = call(getSandboxed,_from)
  135.         local to = call(getSandboxed,_to)
  136.         if fs.isReadOnly(call(resolve,_from)) then
  137.             error("Access denied",2)
  138.         end
  139.         return call(fs.move,from,to)
  140.     end
  141.  
  142.     function newFs.delete(_path)
  143.         local path = call(getSandboxed,_path)
  144.         if fs.isReadOnly(call(resolve,_path)) then
  145.             error("Access denied",2)
  146.         end
  147.     end
  148.  
  149.     function newFs.open(_path,mode)
  150.         local path = call(getSandboxed,_path)
  151.         return call(fs.open,path,mode)
  152.     end
  153.  
  154.     function newFs.list(_path)
  155.         local path = call(resolve,_path)
  156.         if path == "/" then
  157.             local tList=fs.list(sandboxPath)
  158.             for k,v in pairs(fs.list"/") do
  159.                 if fs.isReadOnly(v) then
  160.                     table.insert(tList,v)
  161.                 end
  162.             end
  163.             return tList
  164.         end
  165.         return fs.list(call(getSandboxed,path))
  166.     end
  167.  
  168.     function newFs.find(wildcard)
  169.         assert(type(wildcard)=="string","Expected string",2)
  170.         local tRet = fs.find("/"..fs.combine(sandboxPath,wildcard))
  171.         for k,v in pairs(tRet) do
  172.             if v:sub(1,#sandboxPath)==sandboxPath then
  173.                 tRet[k]=v:sub(#sandboxPath+1,#v)
  174.             end
  175.         end
  176.         for k,v in pairs(fs.find(wildcard)) do
  177.             if fs.isReadOnly(v) then
  178.                 table.insert(tRet,v)
  179.             end
  180.         end
  181.         return tRet
  182.     end
  183.     return newFs
  184. end
  185.  
  186.  
  187. --
  188. --
  189. --
  190.  
  191.  
  192. --Multitasking Module:
  193.  
  194. local function readyToYield(pool)
  195.     for i=1,table.maxn(pool) do
  196.         if pool[i] and #pool[i].events>0 then return false end
  197.     end
  198.     return true
  199. end
  200.  
  201. local function clean(pool)
  202.     for i=1,table.maxn(pool) do
  203.         if pool[i] and pool[i].status=="dead" then
  204.             pool[i]=nil
  205.         end
  206.     end
  207. end
  208.  
  209. local function startTimer(manager,nCo,time)
  210.     local tId=os.startTimer(time)
  211.     manager.timers[tId]=nCo
  212.     return tId
  213. end
  214.  
  215. local function createCoroutine(_co,...)
  216.     local tEvents={{...}}
  217.     local tFilter=nil
  218.     local isPaused=false
  219.     local hasRun=false
  220.     local manager
  221.     if getfenv(_co).os then
  222.         getfenv(_co).os.startTimer=function(t) assert(type(t)=="number","Expected number",2) return manager and startTimer(manager,manager.running,t) end
  223.     end
  224.     local co=coroutine.create(_co)
  225.     local resume=function(self,...)
  226.         if self.status=="dead" then return "dead" end
  227.         local tArgs={...}
  228.         if table.maxn(tArgs)>0 and not isPaused then
  229.             table.insert(tEvents,tArgs)
  230.         end
  231.         if table.maxn(tEvents)>0 then
  232.             if not isPaused then
  233.                 local event=table.remove(tEvents,1) or {}
  234.                 if (tFilter==nil) or (tFilter==event[1]) or event[1]=="terminate" then
  235.                     hasRun=true
  236.                     tFilter=select(2,coroutine.resume(co,unpack(event))) --Spent day debugging, forgot the first return value o coroutine.resume is the success...
  237.                 end
  238.             end
  239.         end
  240.         if self.status=="dead" then return "dead" end
  241.     end
  242.     return setmetatable({
  243.         resume=function(self,...)
  244.             return resume(self,...)
  245.         end,
  246.         passEvent=function(self,...)
  247.             if not isPaused then
  248.                 table.insert(tEvents,{...})
  249.             end
  250.         end,
  251.         kill=function(self)
  252.             rawset(self,"status","dead")
  253.         end,
  254.     },{
  255.         __index=function(self,k)
  256.             if k=="status" then
  257.                 return coroutine.status(co)
  258.             elseif k=="tEvents" or k=="events" then
  259.                 return tEvents
  260.             elseif k=="isPaused" then
  261.                 return isPaused
  262.             elseif k=="hasRun" then
  263.                 return hasRun
  264.             end
  265.         end,
  266.         __newindex=function(self,k,v)
  267.             if k=="isPaused" and type(v)=="boolean" then
  268.                 isPaused=v
  269.             end
  270.             if k=="manager" then
  271.                 manager = v
  272.             end
  273.         end,
  274.         __call=function(self,...)
  275.             return resume(self,...)
  276.         end
  277.     })
  278. end
  279.  
  280. local tManageTemplate = {
  281.     run=function(self,...)
  282.         local tPool=self["tPool"]
  283.         if table.maxn(tPool)==0 then return false end
  284.         for i=1,table.maxn(tPool) do
  285.             if tPool[i] and not tPool[i].hasRun then
  286.                 tPool[i]()
  287.             end
  288.         end
  289.         local firstRun=true
  290.         local tEvents=#{...}>0 and {...} or {coroutine.yield()}
  291.         if tEvents[1]=="timer" and tEvents[2] and self.timers[tEvents[2]] then
  292.             table.insert(self[self.timers[tEvents[2]]].events,{"timer",self})
  293.             firstRun=false
  294.         end
  295.         repeat
  296.             for i=1,table.maxn(tPool) do
  297.                 if (tPool[i] and tPool[i].status~="dead") and (firstRun) then
  298.                     tPool[i]:passEvent(unpack(tEvents))
  299.                 end
  300.                 if tPool[i] and tPool[i].status~="dead" and #tPool[i].events>0 then
  301.                     rawset(self,running,i)
  302.                     if tPool[i]:resume() == "dead" then
  303.                         tPool[i]=nil
  304.                     end
  305.                     rawset(self,running,nil)
  306.                 end
  307.             end
  308.             firstRun=false
  309.         until readyToYield(tPool)
  310.         clean(tPool)
  311.         return table.maxn(tPool)>0
  312.     end,
  313.     run_forever=function(self)
  314.         if not self:run() then return false end
  315.         while self:run() do end
  316.         return true
  317.     end,
  318.     addCo=function(self,co,...)
  319.         self.tPool[table.maxn(self.tPool)+1]=createCoroutine(co,...)
  320.         return table.maxn(self.tPool)
  321.     end,
  322.     timers={},
  323.     getNumForCo=function(self,co)
  324.         for i=1,table.maxn(self) do
  325.             if self.tPool[i]==co then return i end
  326.         end
  327.     end,
  328. }
  329.  
  330. local function newManager(self,main,...)
  331.     local tResults={pcall( function(main,...)
  332.         main=main or function() pcall(loadfile("rom/programs/shell")) end
  333.         local tPool={createCoroutine(main,...)}
  334.         local tEvents={}
  335.         local tManager={}
  336.         setmetatable(tManager,{
  337.         __index=function(t,k)
  338.             if k=="tPool" or k=="pool" then
  339.                 return tPool
  340.             elseif k=="tEvents" or k=="events" then
  341.                 return tEvents
  342.             elseif tonumber(k) then
  343.                 k=tonumber(k)
  344.                 return tPool[k]
  345.             else
  346.                 return tManageTemplate[k]
  347.             end
  348.         end,
  349.         __call=tManageTemplate["run"],
  350.             })
  351.         return tManager
  352.         end, main,...)}
  353.     if tResults[1] then
  354.         return select(2,unpack(tResults))
  355.     else
  356.         return false,"Could not create a new manager!"
  357.     end
  358. end
  359.  
  360. --Start multitasking
  361.  
  362. shell.exit()
  363.  
  364. base.manager = newManager()
  365.  
  366. base.delay = function(time,f,...)
  367.     return base.manager:addCo(function() sleep(time) return call(f,...) end))
  368. end
  369.  
  370. base.manager:run_forever() --Houston, we have liftoff.
Advertisement
Add Comment
Please, Sign In to add comment