Added logging system, fixed filesystem read handle buffering and added read handle seeking.

This commit is contained in:
2025-09-15 08:10:50 +03:00
parent d3d5f21ab1
commit 099fbee8c6
4 changed files with 667 additions and 483 deletions
+15 -1
View File
@@ -4,12 +4,16 @@ _G._OSVERSION = "HALYDE VERSION" -- TODO: Put this in a separate config file
_G._OSLOGO = "" _G._OSLOGO = ""
_G._PUBLIC = {} _G._PUBLIC = {}
_G._PUBLIC.unicode = assert(loadfile("/lib/unicode.lua")(loadfile)) _G._PUBLIC.unicode = assert(loadfile("/lib/unicode.lua")(loadfile))
local log = assert(loadfile("/lib/log.lua")(loadfile))
local handle, tmpdata = filesystem.open("/halyde/config/oslogo.ans", "r"), nil local handle, tmpdata = filesystem.open("/halyde/config/oslogo.ans", "r"), nil
repeat repeat
tmpdata = handle:read(math.huge) tmpdata = handle:read(math.huge)
_OSLOGO = _OSLOGO .. (tmpdata or "") _OSLOGO = _OSLOGO .. (tmpdata or "")
until not tmpdata until not tmpdata
handle:close()
log.kernel.info("Loaded OS logo")
_G.package = { ["preloaded"] = {} } _G.package = { ["preloaded"] = {} }
@@ -26,7 +30,11 @@ function _G.reqgen(load)
modulepath = module modulepath = module
elseif filesystem.exists("/lib/" .. module .. ".lua") then elseif filesystem.exists("/lib/" .. module .. ".lua") then
modulepath = "/lib/" .. module .. ".lua" modulepath = "/lib/" .. module .. ".lua"
elseif shell and shell.workingDirectory and filesystem.exists(filesystem.concat(shell.workingDirectory, module .. ".lua")) then elseif
shell
and shell.workingDirectory
and filesystem.exists(filesystem.concat(shell.workingDirectory, module .. ".lua"))
then
modulepath = shell.workingDirectory .. module .. ".lua" modulepath = shell.workingDirectory .. module .. ".lua"
end end
assert(modulepath, "Module not found\nPossible locations:\n/lib/" .. module .. ".lua") assert(modulepath, "Module not found\nPossible locations:\n/lib/" .. module .. ".lua")
@@ -41,6 +49,7 @@ function _G.reqgen(load)
end end
_G.require = reqgen(_G.load) _G.require = reqgen(_G.load)
log.kernel.info("Generated userland require function")
function _G.package.preload(module) function _G.package.preload(module)
local handle, data, tmpdata = assert(filesystem.open("/lib/" .. module .. ".lua", "r")), "", nil local handle, data, tmpdata = assert(filesystem.open("/lib/" .. module .. ".lua", "r")), "", nil
@@ -53,11 +62,14 @@ function _G.package.preload(module)
_G[module] = nil _G[module] = nil
end end
-- Datatools is imported twice??
require("/halyde/kernel/datatools.lua") -- If this is not imported BEFORE modload gets run, modload requires filesystem which requires computer which requires datatools. TODO: When VFS is implemented, make the pre-VFS loading of filesystem load a more basic version. And remove this. require("/halyde/kernel/datatools.lua") -- If this is not imported BEFORE modload gets run, modload requires filesystem which requires computer which requires datatools. TODO: When VFS is implemented, make the pre-VFS loading of filesystem load a more basic version. And remove this.
log.kernel.info("Loading modules")
require("/halyde/kernel/modload.lua") require("/halyde/kernel/modload.lua")
package.preload("component") package.preload("component")
package.preload("computer") package.preload("computer")
log.kernel.info("Pre-loaded low-level packages")
local component = require("component") local component = require("component")
local gpu = component.gpu local gpu = component.gpu
@@ -65,6 +77,7 @@ local screenAddress = component.list("screen")()
gpu.bind(screenAddress) gpu.bind(screenAddress)
gpu.setResolution(gpu.maxResolution()) gpu.setResolution(gpu.maxResolution())
log.kernel.info("Bound GPU to screen " .. tostring(screenAddress))
if not filesystem.exists("/halyde/config/shell.json") then -- Auto-generate configs if not filesystem.exists("/halyde/config/shell.json") then -- Auto-generate configs
filesystem.copy("/halyde/config/generate/shell.json", "/halyde/config/shell.json") filesystem.copy("/halyde/config/generate/shell.json", "/halyde/config/shell.json")
@@ -73,4 +86,5 @@ if not filesystem.exists("/halyde/config/startupapps.json") then
filesystem.copy("/halyde/config/generate/startupapps.json", "/halyde/config/startupapps.json") filesystem.copy("/halyde/config/generate/startupapps.json", "/halyde/config/startupapps.json")
end end
log.kernel.info("Starting tsched")
require("/halyde/kernel/tsched.lua") require("/halyde/kernel/tsched.lua")
+6
View File
@@ -0,0 +1,6 @@
INFO [1.45] Loaded OS logo
INFO [1.5] Generated userland require function
INFO [1.5] Loading modules
INFO [2.2] Pre-loaded low-level packages
INFO [2.3] Bound GPU to screen d9443671-225d-4637-980f-fad46c9fb845
INFO [2.3] Starting tsched
+180 -57
View File
@@ -1,6 +1,8 @@
local loadfile = ... -- raw loadfile from boot.lua local loadfile = ... -- raw loadfile from boot.lua
local unicode, component, computer local unicode, component, computer
local bufferSize = math.huge or math.maxinteger
if loadfile then if loadfile then
unicode = loadfile("/lib/unicode.lua")(loadfile) unicode = loadfile("/lib/unicode.lua")(loadfile)
component = loadfile("/lib/component.lua")(loadfile) component = loadfile("/lib/component.lua")(loadfile)
@@ -84,7 +86,9 @@ local function readBytes(self,n)
n = n or 1 n = n or 1
if n == 1 then if n == 1 then
local byte = self:read(1) local byte = self:read(1)
if byte==nil then return nil end if byte == nil then
return nil
end
return string.byte(byte) return string.byte(byte)
end end
local bytes, res = { string.byte(self:read(n), 1, n) }, 0 local bytes, res = { string.byte(self:read(n), 1, n) }, 0
@@ -109,7 +113,9 @@ end
local function iterateBytes(self) local function iterateBytes(self)
return function() return function()
local byte = readBytes(self, 1) local byte = readBytes(self, 1)
if byte==nil then self:close() end if byte == nil then
self:close()
end
return byte return byte
end end
end end
@@ -120,19 +126,19 @@ end
function filesystem.makeReadStream(content) function filesystem.makeReadStream(content)
local properHandle = {} local properHandle = {}
local readcursor = 1 local readCursor = 1
function properHandle.read(self, amount) function properHandle.read(self, amount)
checkArg(2, amount, "number") checkArg(2, amount, "number")
local limit = string.len(content) + 1 local limit = string.len(content) + 1
local out = nil local out = nil
if readcursor<limit then if readCursor < limit then
if amount == math.huge then if amount == math.huge then
out = string.sub(content,math.min(readcursor,limit)) out = string.sub(content, math.min(readCursor, limit))
else else
out = string.sub(content,math.min(readcursor,limit),math.min(readcursor+amount-1,limit)) out = string.sub(content, math.min(readCursor, limit), math.min(readCursor + amount - 1, limit))
end end
end end
readcursor=readcursor+amount readCursor = readCursor + amount
if out == "" then if out == "" then
return nil return nil
end end
@@ -186,41 +192,86 @@ function filesystem.open(path, mode, buffered) -- opens a file and returns its h
properHandle.handle = handle properHandle.handle = handle
properHandle.address = address properHandle.address = address
local content = nil local content = nil
local readcursor = 1 local bufferOffset = 0 -- Position in file where buffer starts
if buffered and mode:sub(1,1)=="r" then local readCursor = 1 -- Position within buffer (1-based)
content=""
repeat if buffered and mode == "r" then
tmpdata = component.invoke(address, "read", handle, math.huge or math.maxinteger) content = component.invoke(address, "read", handle, bufferSize) or ""
content = content .. (tmpdata or "") bufferOffset = 0
until not tmpdata readCursor = 1
component.invoke(address, "close", handle)
end end
function properHandle.read(self, amount) function properHandle.read(self, amount)
checkArg(2, amount, "number") checkArg(2, amount, "number")
if unmanagedDrive then if unmanagedDrive then
local sectorIdx = ((readcursor-1)//sectorSize)+1 -- TODO: Test if this still works
if sectorIdx>sectorCount then return nil end local sectorIdx = ((readCursor - 1) // sectorSize) + 1
if sectorIdx > sectorCount then
return nil
end
local sector = unmanagedProxy.readSector(sectorIdx) local sector = unmanagedProxy.readSector(sectorIdx)
local data = sector:sub(((readcursor-1)%sectorSize)+1,((readcursor+math.min(amount,sectorSize)-2)%sectorSize)+1) local data = sector:sub(
readcursor=readcursor+#data ((readCursor - 1) % sectorSize) + 1,
if data=="" then return nil end ((readCursor + math.min(amount, sectorSize) - 2) % sectorSize) + 1
)
readCursor = readCursor + #data
if data == "" then
return nil
end
return data return data
else else
if buffered then if buffered then
local limit = string.len(content)+1 if amount == math.huge or amount == math.maxinteger then
local out = nil -- Read everything remaining
if readcursor<limit then local result = ""
if amount==math.huge then
out = string.sub(content,math.min(readcursor,limit)) -- First, get what's left in current buffer
if content and readCursor <= #content then
result = content:sub(readCursor)
readCursor = #content + 1
end
-- Then read all remaining data from file
while true do
local newData = component.invoke(address, "read", handle, bufferSize)
if not newData or newData == "" then
break
end
result = result .. newData
end
-- Update buffer state
content = nil
bufferOffset = bufferOffset + #(content or "")
return result ~= "" and result or nil
else else
out = string.sub(content,math.min(readcursor,limit),math.min(readcursor+amount-1,limit)) local result = ""
local remaining = amount
while remaining > 0 do
-- If we need more data or buffer is empty
if not content or readCursor > #content then
content = component.invoke(address, "read", handle, bufferSize)
if not content or content == "" then
break
end end
bufferOffset = bufferOffset + (readCursor - 1)
readCursor = 1
end end
readcursor=readcursor+amount
if out=="" then -- Extract data from current buffer
return nil local available = #content - readCursor + 1
local toRead = math.min(remaining, available)
local chunk = content:sub(readCursor, readCursor + toRead - 1)
result = result .. chunk
readCursor = readCursor + toRead
remaining = remaining - toRead
end
return result ~= "" and result or nil
end end
return out
else else
return component.invoke(self.address, "read", self.handle, amount) return component.invoke(self.address, "read", self.handle, amount)
end end
@@ -233,16 +284,26 @@ function filesystem.open(path, mode, buffered) -- opens a file and returns its h
function properHandle.write(self, data) function properHandle.write(self, data)
checkArg(2, data, "string") checkArg(2, data, "string")
if unmanagedDrive then if unmanagedDrive then
local startSector = ((readcursor-1)//sectorSize)+1 local startSector = ((readCursor - 1) // sectorSize) + 1
if startSector>sectorCount then return nil, "not enough space" end if startSector > sectorCount then
local startSByte = ((readcursor-1)%sectorSize)+1 return nil, "not enough space"
local sect = unmanagedProxy.readSector(startSector)
unmanagedProxy.writeSector(startSector,sect:sub(1,startSByte-1)..data:sub(1,sectorSize-startSByte+1))
for i=2,(#data+startSByte)//sectorSize do
if startSector+i-1>sectorCount then return nil, "not enough space" end
unmanagedProxy.writeSector(startSector+i-1,data:sub(startSByte+sectorSize*(i-1),startSByte+sectorSize*i-1))
end end
readcursor=readcursor+#data local startSByte = ((readCursor - 1) % sectorSize) + 1
local sect = unmanagedProxy.readSector(startSector)
unmanagedProxy.writeSector(
startSector,
sect:sub(1, startSByte - 1) .. data:sub(1, sectorSize - startSByte + 1)
)
for i = 2, (#data + startSByte) // sectorSize do
if startSector + i - 1 > sectorCount then
return nil, "not enough space"
end
unmanagedProxy.writeSector(
startSector + i - 1,
data:sub(startSByte + sectorSize * (i - 1), startSByte + sectorSize * i - 1)
)
end
readCursor = readCursor + #data
return true return true
else else
return component.invoke(self.address, "write", self.handle, data) return component.invoke(self.address, "write", self.handle, data)
@@ -251,9 +312,8 @@ function filesystem.open(path, mode, buffered) -- opens a file and returns its h
function properHandle.close(self) function properHandle.close(self)
if buffered then if buffered then
content = nil content = nil
else
return component.invoke(self.address, "close", self.handle)
end end
return component.invoke(self.address, "close", self.handle)
end end
if address == computer.getBootAddress() then if address == computer.getBootAddress() then
local eeprom local eeprom
@@ -285,6 +345,39 @@ function filesystem.open(path, mode, buffered) -- opens a file and returns its h
end end
end end
end end
function properHandle.seek(self, whence, offset)
checkArg(2, whence, "string", "number")
checkArg(3, offset, "number", "nil")
if not offset then
offset = 0
end
if type(whence) == "number" then
offset = whence
end
if not whence or type(whence) == "number" then
whence = "cur"
end
if buffered then
-- Calculate current absolute position in file
local currentAbsolutePos = bufferOffset + readCursor - 1
-- Seek the underlying file handle to the correct position
component.invoke(self.address, "seek", self.handle, "set", currentAbsolutePos)
-- Now perform the actual seek
local newPos = component.invoke(self.address, "seek", self.handle, whence, offset)
-- Invalidate the buffer and reset positions
content = nil
bufferOffset = newPos or 0
readCursor = 1
return newPos
else
return component.invoke(self.address, "seek", self.handle, whence, offset)
end
end
return properHandle return properHandle
end end
@@ -330,7 +423,9 @@ function filesystem.size(path)
if address == computer.getBootAddress() then if address == computer.getBootAddress() then
if absPath:find("^/special/drive") then if absPath:find("^/special/drive") then
local drive = component.get(absPath:sub(16, 18)) local drive = component.get(absPath:sub(16, 18))
if not drive then return false end if not drive then
return false
end
return component.invoke(drive, "getCapacity") return component.invoke(drive, "getCapacity")
elseif absPath:find("^/special/eeprom") then elseif absPath:find("^/special/eeprom") then
local eeprom local eeprom
@@ -363,11 +458,15 @@ local function getRecursiveList(address,absPath)
if component.invoke(address, "isDirectory", absPath .. "/" .. list[i]) then if component.invoke(address, "isDirectory", absPath .. "/" .. list[i]) then
listChanged = true listChanged = true
local dir = list[i] local dir = list[i]
if dir:sub(-1)=="/" then dir=dir:sub(1,-2) end if dir:sub(-1) == "/" then
dir = dir:sub(1, -2)
end
table.insert(dirList, dir) table.insert(dirList, dir)
table.remove(list, i) table.remove(list, i)
local subDir = component.invoke(address, "list", absPath .. "/" .. dir) local subDir = component.invoke(address, "list", absPath .. "/" .. dir)
for j=1,#subDir do table.insert(list,dir.."/"..subDir[j]) end for j = 1, #subDir do
table.insert(list, dir .. "/" .. subDir[j])
end
end end
end end
end end
@@ -375,14 +474,20 @@ local function getRecursiveList(address,absPath)
end end
local function copyContent(fromHandle, toHandle) local function copyContent(fromHandle, toHandle)
if not (fromHandle and toHandle) then return end if not (fromHandle and toHandle) then
return
end
local memory = math.floor(computer.freeMemory() * 0.8) local memory = math.floor(computer.freeMemory() * 0.8)
local tmpdata local tmpdata
while true do while true do
tmpdata = fromHandle:read(memory) tmpdata = fromHandle:read(memory)
if not tmpdata then break end if not tmpdata then
break
end
local status, reason = toHandle:write(tmpdata) local status, reason = toHandle:write(tmpdata)
if status~=true then break end if status ~= true then
break
end
end end
fromHandle:close() fromHandle:close()
toHandle:close() toHandle:close()
@@ -390,8 +495,12 @@ end
local function copyRecursive(fromAddress, fromAbsPath, toAddress, toAbsPath) local function copyRecursive(fromAddress, fromAbsPath, toAddress, toAbsPath)
-- TODO: make this use copyContent -- TODO: make this use copyContent
if fromAbsPath:sub(-1)=="/" then fromAbsPath=fromAbsPath:sub(1,-2) end if fromAbsPath:sub(-1) == "/" then
if toAbsPath:sub(-1)=="/" then toAbsPath=toAbsPath:sub(1,-2) end fromAbsPath = fromAbsPath:sub(1, -2)
end
if toAbsPath:sub(-1) == "/" then
toAbsPath = toAbsPath:sub(1, -2)
end
component.invoke(toAddress, "makeDirectory", toAbsPath) component.invoke(toAddress, "makeDirectory", toAbsPath)
local fileList, dirList = getRecursiveList(fromAddress, fromAbsPath) local fileList, dirList = getRecursiveList(fromAddress, fromAbsPath)
for i = 1, #dirList do for i = 1, #dirList do
@@ -412,11 +521,19 @@ local function copyRecursive(fromAddress,fromAbsPath,toAddress,toAbsPath)
local fromHandle = component.invoke(fromAddress, "open", fromFile, "r") local fromHandle = component.invoke(fromAddress, "open", fromFile, "r")
local toHandle = component.invoke(toAddress, "open", toFile, "w") local toHandle = component.invoke(toAddress, "open", toFile, "w")
copyContent({ copyContent({
["read"]=function(...) return component.invoke(fromAddress, "read", handle, ...) end, ["read"] = function(...)
["close"]=function(...) return component.invoke(fromAddress, "close", handle, ...) end return component.invoke(fromAddress, "read", handle, ...)
end,
["close"] = function(...)
return component.invoke(fromAddress, "close", handle, ...)
end,
}, { }, {
["write"]=function(...) return component.invoke(fromAddress, "write", handle, ...) end, ["write"] = function(...)
["close"]=function(...) return component.invoke(fromAddress, "close", handle, ...) end return component.invoke(fromAddress, "write", handle, ...)
end,
["close"] = function(...)
return component.invoke(fromAddress, "close", handle, ...)
end,
}) })
end end
end end
@@ -488,9 +605,15 @@ function filesystem.remove(path)
if not address then if not address then
return false return false
end end
if absPath:find("^/special") then return false end if absPath:find("^/special") then
if absPath:find("^/tmp") then return false end return false
if absPath:find("^/mnt") then return false end end
if absPath:find("^/tmp") then
return false
end
if absPath:find("^/mnt") then
return false
end
return component.invoke(address, "remove", absPath) return component.invoke(address, "remove", absPath)
end end
@@ -503,4 +626,4 @@ function filesystem.makeDirectory(path)
return component.invoke(address, "makeDirectory", absPath) return component.invoke(address, "makeDirectory", absPath)
end end
return(filesystem) return filesystem
+66 -25
View File
@@ -1,30 +1,71 @@
local fs = require("filesystem") local fs, computer
local computer = require("computer") if require then
fs = require("filesystem")
computer = require("computer")
else
local loadfile = ...
fs = loadfile("/lib/filesystem.lua")(loadfile)
computer = _G.computer
end
logFileSizeLimit = 16384
local function writeToLog(path, text)
local handle
if fs.exists(path) then
handle = assert(fs.open(path, "a"))
else
handle = assert(fs.open(path, "w"))
end
handle:write(text .. "\n")
handle:close()
-- Log trimming if it gets too long
if fs.size(path) > logFileSizeLimit then
ocelot.log("Trimming log...")
local newlineCounter = 0
local sizeCounter = 0
local readHandle = fs.open(path, "r")
local chunkSize = 1024
readHandle:seek("end", -chunkSize)
repeat
local readText = readHandle:read(chunkSize)
readHandle:seek(-chunkSize * 2)
local _, newlineCount = readText:gsub("\n", "\n")
newlineCounter = newlineCounter + newlineCount
sizeCounter = sizeCounter + chunkSize
until sizeCounter >= logFileSizeLimit * 0.75
readHandle:seek(chunkSize)
local writeHandle = fs.open(path, "w")
while true do
local tmpdata = readHandle:read(math.huge or math.maxinteger)
if not tmpdata then
break
end
writeHandle:write(tmpdata)
end
readHandle:close()
writeHandle:close()
end
end
local log = {} local log = {}
function log.add(text, logType) setmetatable(log, {
checkArg(1, text, "string") ["__index"] = function(tab, index)
checkArg(2, logType, "string", "nil") return {
if logType ~= "debug" and logType ~= "info" and logType ~= "warning" and logType ~= "error" and logType then ["logpath"] = fs.concat("/halyde/logs/", index .. ".log"),
error("Log type must either be debug, info, warning or error.") ["info"] = function(text)
end writeToLog(fs.concat("/halyde/logs/", index .. ".log"), "INFO [" .. computer.uptime() .. "] " .. text)
if not logType then end,
logType = "debug" ["warn"] = function(text)
end writeToLog(fs.concat("/halyde/logs/", index .. ".log"), "WARN [" .. computer.uptime() .. "] " .. text)
local handle = fs.open("/halyde/system.log", "a") end,
local time = computer.uptime() ["error"] = function(text)
local logText = string.format("[%02d:%02d:%02d:%02d] " .. text, math.floor(time / 86400), math.floor(time / 3600 % 24), math.floor(time / 60 % 60), math.floor(time % 60)) .. "\n" writeToLog(fs.concat("/halyde/logs/", index .. ".log"), "ERROR [" .. computer.uptime() .. "] " .. text)
if logType == "debug" then end,
handle:write("\27[37m" .. logText) }
elseif logType == "info" then end,
handle:write("\27[97m" .. logText) })
elseif logType == "warning" then
handle:write("\27[93m" .. logText)
else
handle:write("\27[91m" .. logText)
end
handle:close()
end
return log return log