Tag365

ServerFS autorun.lua

Feb 14th, 2016
2,900
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 14.23 KB | None | 0 0
  1. ----------------------------------
  2. -- ServerFS Host Autorun Script --
  3. ----------------------------------
  4. -- Configuration --
  5. local cachedActions = true -- Set this to true to make certain actions cached
  6. local mordalTextWindow = true -- Set this to true to make the console mordal and clear the terminal.
  7. local sendResponseDirectly = true -- Set this to true if this computer will send responses to clients without the assistance of another computer.
  8. local showMessagesReceived = true -- Set this to true to print messages to the terminal.
  9. local showReturnedVariables = true
  10. local forceReadOnly = false
  11. local fsLabelCanBeChanged = false -- Set this to true to allow changes to the filesystem label.
  12. local handleFix = true -- Set this to true if OpenComputers is updated to a version which does not use number identifation for file handles.
  13. local autoCheckVersion = true
  14. local fsLabel = "Server filesystem"
  15.  
  16. -- Modules --
  17. local computer = require("computer")
  18. local component = require("component")
  19. local serialization = require("serialization")
  20. local event = require("event")
  21. local term = require("term")
  22.  
  23. -- Clear anything on the screen
  24. if mordalTextWindow then
  25.     term.clear()
  26.     term.setCursor(1, 1)
  27.     term.setCursorBlink(false)
  28. end
  29.  
  30. local freeMemory = function()
  31.     for ilteration = 1, 10 do
  32.         os.sleep(0)
  33.     end
  34. end
  35. freeMemory() -- Free memory
  36.  
  37. -----------------------
  38. -- Set up the server --
  39. -----------------------
  40. -- Gets a primary component.
  41. local getComponent
  42. getComponent = function(componentType)
  43.     local success, component = pcall(component.getPrimary, componentType)
  44.     if success then
  45.         return component
  46.     end
  47.     return false, component
  48. end
  49.  
  50. local filesystems = {}
  51.  
  52. -- Find filesystems --
  53. print("Finding filesystems...")
  54. local isReadOnly = true
  55. for address in component.list("filesystem") do
  56.     local thisComponent = component.proxy(address)
  57.     -- We do not want to use the filesystem on the temp address or the boot address.
  58.     if address ~= computer.tmpAddress() and address ~= computer.getBootAddress() then
  59.         filesystems[address] = thisComponent
  60.         isReadOnly = forceReadOnly or (isReadOnly and thisComponent.isReadOnly())
  61.         if not thisComponent.getLabel() or thisComponent.getLabel() == "" then
  62.             thisComponent.setLabel("ServerFSDisk " .. address:sub(1, 3))
  63.         end
  64.     end
  65. end
  66.  
  67. -- Check if there is at least one filesystem for this server to use --
  68. local hasFilesystem = false
  69. for key, value in pairs(filesystems) do
  70.     hasFilesystem = true
  71.     break
  72. end
  73. if not hasFilesystem then
  74.     error("No usable filesystems detected. ServerFS requires at least one filesystem not used for booting the system to run.", 0)
  75.     while true do
  76.         freeMemory()
  77.     end
  78. end
  79.  
  80. -- Build the filesystem variables --
  81. local spaceTotalCache, spaceFreeCache, spaceUsedCache -- Respective caches
  82. local listCache -- The directory list cache
  83. local fileToFS = {} -- Which filesystem to access to find which file.
  84. local handleToFS = {} -- Which filesystem to access for each handle.
  85. local handleToNumber = {}
  86. local numberToHandle = {}
  87. local serverFS = {type = "filesystem", address = "0000-ServerFS"} -- The filesystem module itself.
  88.  
  89. -- Invalidates the caches.
  90. local invalidateCache = function()
  91.     spaceTotalCache, spaceFreeCache, spaceUsedCache = nil, nil, nil
  92.     if cachedActions then
  93.         listCache = {}
  94.     end
  95. end
  96. if cachedActions then
  97.     listCache = {}
  98. end
  99.  
  100. -- Accoiates files to the correct file system
  101. local numOfFiles = 0
  102. local associateFilesInSubDirectories
  103. associateFilesInSubDirectories = function(filesystem, path)
  104.     local itemsInDirectory = filesystem.list(path)
  105.     for key, item in ipairs(itemsInDirectory) do
  106.         if filesystem.isDirectory(path..item) then
  107.             associateFilesInSubDirectories(filesystem, path..item)
  108.         end
  109.         fileToFS[path..item] = filesystem
  110.         numOfFiles = numOfFiles + 1
  111.     end
  112.     term.setCursor(1, 3)
  113.     term.clearLine()
  114.     print(numOfFiles..(numOfFiles == 1 and " file" or " files").." found")
  115. end
  116.  
  117. -- Ilterate through the filesystems, to find all files in them.
  118. print("Finding files...")
  119. for address, filesystem in pairs(filesystems) do
  120.     associateFilesInSubDirectories(filesystem, "")
  121. end
  122.  
  123. -- Print the number of files
  124. term.setCursor(1, 3)
  125. term.clearLine()
  126. print(numOfFiles..(numOfFiles == 1 and " file" or " files").." found")
  127.  
  128. freeMemory() -- Free memory
  129.  
  130. -- Build the library --
  131. print("Initalizating library...")
  132.  
  133. -- The overall capacity of the file system, in bytes.
  134. function serverFS.spaceTotal()
  135.     if spaceTotalCache then
  136.         return spaceTotalCache
  137.     end
  138.    
  139.     -- Calculate the total space
  140.     local spaceTotal = 0
  141.     for address, filesystem in pairs(filesystems) do
  142.         spaceTotal = spaceTotal + filesystem.spaceTotal()
  143.     end
  144.    
  145.     spaceTotalCache = spaceTotal
  146.    
  147.     return tonumber(spaceTotal)
  148. end
  149.  
  150. -- The currently used capacity of the file system, in bytes.
  151. function serverFS.spaceUsed()
  152.     if spaceUsedCache then
  153.         return spaceUsedCache
  154.     end
  155.    
  156.     -- Calculate the used space
  157.     local spaceUsed = 0
  158.     for address, filesystem in pairs(filesystems) do
  159.         spaceUsed = spaceUsed + filesystem.spaceUsed()
  160.     end
  161.    
  162.     spaceUsedCache = spaceUsed
  163.    
  164.     return tonumber(spaceUsed)
  165. end
  166.  
  167. -- The amount of space free on the file system.
  168. function serverFS.spaceFree()
  169.     if spaceFreeCache then
  170.         return spaceFreeCache
  171.     end
  172.    
  173.     -- Calculate the total and used space
  174.     local spaceTotal, spaceUsed = 0, 0
  175.     for address, filesystem in pairs(filesystems) do
  176.         spaceTotal = spaceTotal + filesystem.spaceTotal()
  177.         spaceUsed = spaceUsed + filesystem.spaceUsed()
  178.     end
  179.    
  180.     spaceFreeCache = tonumber(spaceTotal - spaceUsed)
  181.     spaceUsedCache = spaceUsed
  182.     spaceTotalCache = spaceTotal
  183.    
  184.     return tonumber(spaceTotal - spaceUsed)
  185. end
  186.  
  187. -- Returns whether the file system is read-only.
  188. function serverFS.isReadOnly()
  189.     return isReadOnly
  190. end
  191.  
  192. -- Finds the filesystem to host this path.
  193. -- If a file exists at this path on a filesystem then return the filesystem it is hosted on.
  194. -- If not, then return the filesystem with the most space free.
  195. local findFSFromPath = function(path)
  196.     if fileToFS[path] then
  197.         return fileToFS[path]
  198.     end
  199.    
  200.     -- Find a filesystem to host this file.
  201.     local largestFreeSpace, usedFileSystem = 0
  202.     for address, filesystem in pairs(filesystems) do
  203.         local spaceAvailable = filesystem.spaceTotal() - filesystem.spaceUsed()
  204.         if spaceAvailable > largestFreeSpace then
  205.             largestFreeSpace, usedFileSystem = spaceAvailable, filesystem
  206.         end
  207.     end
  208.     fileToFS[path] = usedFileSystem
  209.    
  210.     return fileToFS[path]
  211. end
  212.  
  213. -- Opens a new file descriptor and returns its handle.
  214. function serverFS.open(path, mode)
  215.     local filesystem = findFSFromPath(path)
  216.  
  217.     local handle, errorMessage = filesystem.open(path, mode)
  218.     if handle then
  219.         handleToFS[handle] = filesystem
  220.         if autoCheckVersion then
  221.             if type(handle) == "table" then
  222.                 handleFix = true
  223.             else
  224.                 handleFix = false
  225.             end
  226.             autoCheckVersion = false
  227.         end
  228.         if handleFix and handle then
  229.             handleToNumber[handle] = math.floor(math.random()*90000)
  230.             numberToHandle[handleToNumber[handle]] = handle
  231.             return handleToNumber[handle], errorMessage
  232.         else
  233.             return handle, errorMessage
  234.         end
  235.     end
  236.    
  237.     return tonumber(handle), errorMessage
  238. end
  239.  
  240. -- Creates a directory at the specified absolute path in the file system. Creates parent directories, if necessary.
  241. function serverFS.makeDirectory(path)
  242.     local success, errorMessage
  243.     for address, filesystem in pairs(filesystems) do
  244.         success, errorMessage = filesystem.makeDirectory(path)
  245.     end
  246.     invalidateCache()
  247.     return success, errorMessage
  248. end
  249.  
  250. -- Creates a directory at the specified absolute path in the file system. Creates parent directories, if necessary.
  251. function serverFS.exists(path)
  252.     local filesystem = findFSFromPath(path)
  253.     return filesystem.exists(path)
  254. end
  255.  
  256. -- Returns a list of names of objects in the directory at the specified absolute path in the file system.
  257. function serverFS.list(path)
  258.     if listCache then
  259.         if listCache[path] then
  260.             return listCache[path]
  261.         end
  262.     end
  263.     local completeList = {}
  264.     for address, filesystem in pairs(filesystems) do
  265.         local list = filesystem.list(path)
  266.         if type(list) == "table" then
  267.             for key, value in ipairs(list) do
  268.                 completeList[#completeList + 1] = value
  269.             end
  270.         end
  271.     end
  272.    
  273.     if listCache then
  274.         listCache[path] = completeList
  275.     end
  276.    
  277.     return completeList
  278. end
  279.  
  280. -- Removes the object at the specified absolute path in the file system.
  281. function serverFS.remove(path)
  282.     local filesystem = findFSFromPath(path)
  283.     invalidateCache()
  284.     return filesystem.remove(path)
  285. end
  286.  
  287. -- Returns whether the object at the specified absolute path in the file system is a directory.
  288. function serverFS.isDirectory(path)
  289.     local filesystem = findFSFromPath(path)
  290.     return filesystem.isDirectory(path)
  291. end
  292.  
  293. -- Returns the size of the object at the specified absolute path in the file system.
  294. function serverFS.size(path)
  295.     local filesystem = findFSFromPath(path)
  296.     return tonumber(filesystem.size(path))
  297. end
  298.  
  299. -- Returns the (real world) timestamp of when the object at the specified absolute path in the file system was modified.
  300. function serverFS.lastModified(path)
  301.     local filesystem = findFSFromPath(path)
  302.     return tonumber(filesystem.lastModified(path))
  303. end
  304.  
  305. -- Renames a file.
  306. function serverFS.rename(path, newPath)
  307.     local filesystem = findFSFromPath(path)
  308.     invalidateCache()
  309.     return filesystem.rename(path, newPath)
  310. end
  311.  
  312. -- Get the current label of the file system.
  313. function serverFS.getLabel()
  314.     return fsLabel
  315. end
  316.  
  317. -- Sets the label of the file system. Returns the new value, which may be truncated.
  318. function serverFS.setLabel(newLabel)
  319.     if fsLabelCanBeChanged then
  320.         if type(newLabel) == "string" then
  321.             fsLabel = newLabel:sub(1, 80)
  322.         end
  323.     end
  324.     return fsLabel
  325. end
  326.  
  327. -- Seeks in an open file descriptor with the specified handle. Returns the new pointer position.
  328. function serverFS.seek(handle, ...)
  329.     if handleFix then
  330.         handle = numberToHandle[handle]
  331.     end
  332.     if handleToFS[handle] then
  333.         return handleToFS[handle].seek(handle, ...)
  334.     end
  335. end
  336.  
  337. -- Writes the specified data to an open file descriptor with the specified handle.
  338. function serverFS.write(handle, ...)
  339.     if handleFix then
  340.         handle = numberToHandle[handle]
  341.     end
  342.     if handleToFS[handle] then
  343.         return handleToFS[handle].write(handle, ...)
  344.     end
  345. end
  346.  
  347. -- Reads up to the specified amount of data from an open file descriptor with the specified handle. Returns nil when EOF is reached.
  348. function serverFS.read(handle, ...)
  349.     if handleFix then
  350.         handle = numberToHandle[handle]
  351.     end
  352.     if handleToFS[handle] then
  353.         return handleToFS[handle].read(handle, ...)
  354.     end
  355. end
  356.  
  357. -- Closes an open file descriptor with the specified handle.
  358. function serverFS.close(handle, ...)
  359.     if handleFix then
  360.         handle = numberToHandle[handle]
  361.     end
  362.     if handleToFS[handle] then
  363.         invalidateCache()
  364.         return handleToFS[handle].close(handle, ...)
  365.     end
  366. end
  367.  
  368. package.loaded["serverFS"] = serverFS
  369.  
  370. -- Free up unused memory --
  371. print("Freeing memory...")
  372. freeMemory() -- Free memory
  373.  
  374. print("Total array capacity is "..serverFS.spaceTotal().." bytes")
  375. print("Used array capacity is "..serverFS.spaceUsed().." bytes")
  376. print("Free space on array totals "..serverFS.spaceFree().." bytes")
  377. print("Total RAM is "..computer.totalMemory().." bytes")
  378. print("Remaining memory is "..computer.freeMemory().." bytes")
  379.  
  380. -- Set the ports used --
  381. local directResponseReceivePort = 300
  382. local directResponseSendPort = 280
  383. local indirectReceivePort = 250
  384. local indirectSendPort = 245
  385.  
  386. local sendPort = (sendResponseDirectly and directResponseSendPort) or indirectSendPort
  387. local receivePort = (sendResponseDirectly and directResponseReceivePort) or indirectReceivePort
  388.  
  389. -- Get the components used for the messaging system --
  390. local modemAddress, tunnelAddress
  391. local modem = getComponent("modem") -- get primary modem component
  392. if modem then
  393.     modem.open(receivePort)
  394.     modem.broadcast(240, "Server Filesystem online")
  395.     modem.setWakeMessage("Server Filesystem online")
  396.     modemAddress = modem.address
  397. end
  398.  
  399. local tunnel = getComponent("tunnel") -- get primary tunnel component
  400. if tunnel then
  401.     tunnelAddress = tunnel.address
  402. end
  403.  
  404. -- Check for a modem or tunnel
  405. if not (tunnel or modem) then
  406.     error("No modem or tunnel detected. ServerFS requires a modem or tunnel to work.")
  407. end
  408.  
  409. -- Activate the messaging system --
  410. print("Activating message system...")
  411.  
  412. local lastMessage, lastMessage2 -- This is used to stop duplicate packets from being sent to relays and cause a cycle.
  413. local lastTime = -.1
  414.  
  415. print("Server ready to handle requests.")
  416. if showMessagesReceived then
  417.     print("Waiting for message...")
  418. end
  419. -- Start recieving messages --
  420. while true do
  421.     local _, componentAddress, from, port, _, message = event.pull("modem_message")
  422.    
  423.     if port == receivePort or componentAddress == tunnelAddress then
  424.         if #message > 1 and ((message ~= lastMessage and message ~= lastMessage2) or computer.uptime() - lastTime > .1) then
  425.             if showMessagesReceived then
  426.                 print("Got a message from " .. from .. ": " .. tostring(message))
  427.             end
  428.            
  429.             -- Attempt to handle the request
  430.             local recievedTime = computer.uptime()
  431.             local arguments = serialization.unserialize(message)
  432.            
  433.             -- Did we even get the arguments???
  434.             if arguments then
  435.                 if serverFS[arguments[1]] then
  436.                     local functToCall = serverFS[arguments[1]]
  437.                     table.remove(arguments, 1)
  438.                     local result = {pcall(functToCall, table.unpack(arguments))}
  439.                     if not result[1] then
  440.                         io.stderr:write("Failed to handle request: "..tostring(result[2]).."\n")
  441.                     end
  442.                    
  443.                     if showReturnedVariables then
  444.                         print(result[1], result[2], result[3])
  445.                     end
  446.                    
  447.                     -- Allow the computer to get the message
  448.                     if computer.uptime() - recievedTime < .1 then
  449.                         os.sleep(.1)
  450.                     end
  451.                     if componentAddress == modemAddress then
  452.                         modem.send(from, sendPort, serialization.serialize(result))
  453.                     elseif componentAddress == tunnelAddress then
  454.                         tunnel.send(serialization.serialize(result))
  455.                     end
  456.                 else
  457.                     if componentAddress == modemAddress then
  458.                         modem.send(from, sendPort, serialization.serialize({false, "function does not exist"}))
  459.                     elseif componentAddress == tunnelAddress then
  460.                         tunnel.send(serialization.serialize({false, "function does not exist"}))
  461.                     end
  462.                 end
  463.             end
  464.            
  465.             lastMessage2 = lastMessage
  466.             lastMessage = message
  467.             lastTime = computer.uptime()
  468.         end
  469.     end
  470. end
Add Comment
Please, Sign In to add comment