10 Commits

Author SHA1 Message Date
100b23793c Merge pull request 'Many changes' (#62) from Kan18/develop-1.8 into develop-1.8
Some checks failed
CI / build (push) Has been cancelled
Reviewed-on: #62
2026-03-22 11:58:40 -04:00
Kan18
9b502553e0 Update json.lua 2023-12-17 10:14:40 -05:00
Kan18
c247097e20 oops 2022-12-28 20:25:52 +04:00
Kan18
2597724dab Fix Util.getVersion 2022-12-28 20:05:16 +04:00
Kan18
5f153721ea oops 2022-12-28 19:26:48 +04:00
Kan18
0a42551ab7 actually set it to 30 2022-12-28 19:22:38 +04:00
Kan18
76b310d873 oops 2022-12-28 19:22:20 +04:00
Kan18
f26f443b9d Increase discovery message interval, distribute messages
Previously on large server(s), there was an issue where because
chunkloaded computers all started up at once, so they all had the same
discovery message sending timer. This prevents that by starting each off
with a random offset. Also increases the interval for sending messages
from 15 s to 30 s, so that messages (which are the same, a lot of the
time) get sent less often.
2022-12-28 18:43:49 +04:00
Kan18
8fbcc7b8bc Sanitize label in samba.lua
Prevent labels from having .., /, *, control characters, or quotes (this
generally messes things up) and limit them to a reasonable length. We
might possibly also want to do this in snmp.lua, I'm not sure if that
will break things though
2022-12-28 18:26:27 +04:00
Kan18
f7ba900930 genotp.lua changes
Make genotp more clearly state that the password can be used once but
allows permanent access (sorry for not making this clear earlier.) Use a
slightly longer password, and allow uppercase characters except for some
that could be ambiguous
2022-12-28 13:22:57 +04:00
17 changed files with 82 additions and 304 deletions

View File

@@ -51,7 +51,7 @@ local config = {
} }
Config.load('Overview', config) Config.load('Overview', config)
local extSupport = Util.getVersion() >= 1.76 local extSupport = Util.supportsExtChars()
local applications = { } local applications = { }
local buttons = { } local buttons = { }

View File

@@ -1,14 +1,22 @@
local SHA = require("opus.crypto.sha2") local SHA = require("opus.crypto.sha2")
local acceptableCharacters = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"} local acceptableCharacters = {}
for c = 0, 127 do
local char = string.char(c)
-- exclude potentially ambiguous characters
if char:match("[1-9a-zA-Z]") and char:match("[^OIl]") then
table.insert(acceptableCharacters, char)
end
end
local acceptableCharactersLen = #acceptableCharacters local acceptableCharactersLen = #acceptableCharacters
local password = "" local password = ""
for _i = 1, 8 do for i = 1, 10 do
password = password .. acceptableCharacters[math.random(acceptableCharactersLen)] password = password .. acceptableCharacters[math.random(acceptableCharactersLen)]
end end
os.queueEvent("set_otp", SHA.compute(password)) os.queueEvent("set_otp", SHA.compute(password))
print("Your one-time password is: " .. password) print("This allows one other device to permanently gain access to this device.")
print("Use the trust settings in System to revert this.")
print("Your one-time password is: " .. password)

View File

@@ -1,200 +0,0 @@
-- Memory Profiler for CC:Tweaked / Opus OS
-- Usage: memprofile [--watch] [--interval <seconds>]
--
-- Shows current Lua memory usage with breakdown estimates.
-- Use --watch to continuously monitor.
-- Useful for detecting memory leaks and understanding overhead.
local args = { ... }
local watch = false
local interval = 3
for i, arg in ipairs(args) do
if arg == '--watch' or arg == '-w' then
watch = true
elseif arg == '--interval' or arg == '-i' then
interval = tonumber(args[i + 1]) or 3
elseif arg == '--help' or arg == '-h' then
print('Usage: memprofile [--watch] [--interval <secs>]')
print('')
print('Options:')
print(' --watch, -w Continuously monitor memory')
print(' --interval, -i N Update interval in seconds (default: 3)')
print(' --help, -h Show this help')
return
end
end
local term = _G.term
local os = _G.os
local function formatBytes(bytes)
if bytes < 1024 then
return string.format('%d B', bytes)
elseif bytes < 1024 * 1024 then
return string.format('%.1f KB', bytes / 1024)
else
return string.format('%.2f MB', bytes / (1024 * 1024))
end
end
local function countTable(t, seen)
if type(t) ~= 'table' or seen[t] then return 0, 0 end
seen[t] = true
local entries = 0
local nested = 0
for k, v in pairs(t) do
entries = entries + 1
if type(v) == 'table' then
local e, n = countTable(v, seen)
nested = nested + 1 + e
entries = entries + n
end
if type(k) == 'table' then
local e, n = countTable(k, seen)
nested = nested + 1 + e
entries = entries + n
end
end
return entries, nested
end
local function getSnapshot()
-- Force a full GC cycle to get accurate usage
collectgarbage('collect')
collectgarbage('collect')
local memKB = collectgarbage('count') -- returns KB as float
local snapshot = {
totalKB = memKB,
totalBytes = math.floor(memKB * 1024),
timestamp = os.clock(),
}
-- Count entries in major global tables
local seen = {}
local globals = {}
local interesting = {
{ name = '_G (globals)', tbl = _G },
{ name = 'kernel', tbl = _G.kernel },
{ name = 'network', tbl = _G.network },
{ name = 'device', tbl = _G.device },
}
for _, item in ipairs(interesting) do
if type(item.tbl) == 'table' then
local entries, nested = countTable(item.tbl, seen)
table.insert(globals, {
name = item.name,
entries = entries,
nested = nested,
})
end
end
snapshot.globals = globals
-- Count routines if kernel is available
if _G.kernel and _G.kernel.routines then
snapshot.routines = #_G.kernel.routines
end
-- Count loaded modules
if package and package.loaded then
local count = 0
for _ in pairs(package.loaded) do
count = count + 1
end
snapshot.loadedModules = count
end
return snapshot
end
local function printSnapshot(snap, prev)
term.clear()
term.setCursorPos(1, 1)
local w = term.getSize()
local sep = string.rep('-', w)
term.setTextColor(colors.yellow)
print('=== Memory Profile ===')
term.setTextColor(colors.white)
print('')
-- Total memory
local memStr = formatBytes(snap.totalBytes)
local deltaStr = ''
if prev then
local delta = snap.totalBytes - prev.totalBytes
if delta > 0 then
deltaStr = string.format(' (+%s)', formatBytes(delta))
term.setTextColor(colors.red)
elseif delta < 0 then
deltaStr = string.format(' (-%s)', formatBytes(-delta))
term.setTextColor(colors.green)
end
end
term.setTextColor(colors.white)
print(string.format('Total Memory: %s%s', memStr, deltaStr))
print(string.format('Uptime: %.1fs', snap.timestamp))
print('')
-- Table sizes
term.setTextColor(colors.lightBlue)
print('Global Tables:')
term.setTextColor(colors.white)
print(sep)
print(string.format(' %-20s %8s %8s', 'Name', 'Entries', 'Nested'))
print(sep)
for _, g in ipairs(snap.globals) do
print(string.format(' %-20s %8d %8d', g.name, g.entries, g.nested))
end
print(sep)
print('')
-- Kernel info
if snap.routines then
term.setTextColor(colors.lightBlue)
print('Kernel:')
term.setTextColor(colors.white)
print(string.format(' Active routines: %d', snap.routines))
end
if snap.loadedModules then
print(string.format(' Loaded modules: %d', snap.loadedModules))
end
print('')
-- CC:Tweaked limits
term.setTextColor(colors.gray)
print('Note: CC:Tweaked default memory limit is ~128MB per computer.')
print('High memory usage may cause slowdowns or crashes.')
if watch then
print('')
term.setTextColor(colors.yellow)
print(string.format('Refreshing every %ds... (Ctrl+T to stop)', interval))
end
end
local function run()
local prev = nil
if watch then
while true do
local snap = getSnapshot()
printSnapshot(snap, prev)
prev = snap
os.sleep(interval)
end
else
local snap = getSnapshot()
printSnapshot(snap, nil)
end
end
run()

View File

@@ -59,6 +59,10 @@ local function sambaConnection(socket)
print('samba: Connection closed') print('samba: Connection closed')
end end
local function sanitizeLabel(computer)
return (computer.id.."_"..computer.label:gsub("[%c%.\"'/%*]", "")):sub(1, 40)
end
Event.addRoutine(function() Event.addRoutine(function()
print('samba: listening on port 139') print('samba: listening on port 139')
@@ -79,10 +83,10 @@ Event.addRoutine(function()
end) end)
Event.on('network_attach', function(_, computer) Event.on('network_attach', function(_, computer)
fs.mount(fs.combine('network', computer.label), 'netfs', computer.id) fs.mount(fs.combine('network', sanitizeLabel(computer)), 'netfs', computer.id)
end) end)
Event.on('network_detach', function(_, computer) Event.on('network_detach', function(_, computer)
print('samba: detaching ' .. computer.label) print('samba: detaching ' .. sanitizeLabel(computer))
fs.unmount(fs.combine('network', computer.label)) fs.unmount(fs.combine('network', sanitizeLabel(computer)))
end) end)

View File

@@ -152,7 +152,7 @@ local function getSlots()
end end
local function sendInfo() local function sendInfo()
if os.clock() - infoTimer >= 1 then -- don't flood if os.clock() - infoTimer >= 5 then -- don't flood
infoTimer = os.clock() infoTimer = os.clock()
info.label = os.getComputerLabel() info.label = os.getComputerLabel()
info.uptime = math.floor(os.clock()) info.uptime = math.floor(os.clock())
@@ -194,16 +194,25 @@ local function sendInfo()
end end
end end
-- every 10 seconds, send out this computer's info local function cleanNetwork()
Event.onInterval(10, function()
sendInfo()
for _,c in pairs(_G.network) do for _,c in pairs(_G.network) do
local elapsed = os.clock()-c.timestamp local elapsed = os.clock()-c.timestamp
if c.active and elapsed > 15 then if c.active and elapsed > 50 then
c.active = false c.active = false
os.queueEvent('network_detach', c) os.queueEvent('network_detach', c)
end end
end end
end
-- every 30 seconds, send out this computer's info
-- send with offset so that messages are evenly distributed and do not all come at once
Event.onTimeout(math.random() * 30, function()
sendInfo()
cleanNetwork()
Event.onInterval(30, function()
sendInfo()
cleanNetwork()
end)
end) end)
Event.on('turtle_response', function() Event.on('turtle_response', function()
@@ -213,4 +222,5 @@ Event.on('turtle_response', function()
end end
end) end)
Event.onTimeout(1, sendInfo) -- send info early so that computers show soon after booting
Event.onTimeout(math.random() * 2 + 1, sendInfo)

View File

@@ -1,9 +1,10 @@
local Security = require('opus.security') local Security = require('opus.security')
local SHA = require('opus.crypto.sha2')
local Terminal = require('opus.terminal') local Terminal = require('opus.terminal')
local password = Terminal.readPassword('Enter new password: ') local password = Terminal.readPassword('Enter new password: ')
if password then if password then
Security.updatePassword(password) Security.updatePassword(SHA.compute(password))
print('Password updated') print('Password updated')
end end

View File

@@ -26,12 +26,12 @@ return UI.Tab {
x = 2, y = 5, ex = -2, ey = -2, x = 2, y = 5, ex = -2, ey = -2,
values = { values = {
{ name = '', value = '' }, { name = '', value = '' },
{ name = 'CC version', value = Util.getVersion() }, { name = 'CC version', value = ("%d.%d"):format(Util.getVersion()) },
{ name = 'Lua version', value = _VERSION }, { name = 'Lua version', value = _VERSION },
{ name = 'MC version', value = Util.getMinecraftVersion() }, { name = 'MC version', value = Util.getMinecraftVersion() },
{ name = 'Disk free', value = Util.toBytes(fs.getFreeSpace('/')) }, { name = 'Disk free', value = Util.toBytes(fs.getFreeSpace('/')) },
{ name = 'Computer ID', value = tostring(os.getComputerID()) }, { name = 'Computer ID', value = tostring(os.getComputerID()) },
{ name = 'Day', value = tostring(os.day()) }, { name = 'Day', value = tostring(os.day()) },
}, },
disableHeader = true, disableHeader = true,
inactive = true, inactive = true,

View File

@@ -1,5 +1,5 @@
sys/apps/pain.lua urlfs https://github.com/LDDestroier/CC/raw/master/pain.lua sys/apps/pain.lua urlfs https://github.com/LDDestroier/CC/raw/master/pain.lua
sys/apps/update.lua urlfs https://pastebin.com/raw/UzGHLbNC sys/apps/update.lua urlfs http://pastebin.com/raw/UzGHLbNC
sys/apps/Enchat.lua urlfs https://raw.githubusercontent.com/LDDestroier/enchat/master/enchat3.lua sys/apps/Enchat.lua urlfs https://raw.githubusercontent.com/LDDestroier/enchat/master/enchat3.lua
sys/apps/cloud.lua urlfs https://cloud-catcher.squiddev.cc/cloud.lua sys/apps/cloud.lua urlfs https://cloud-catcher.squiddev.cc/cloud.lua
rom/modules/main/opus linkfs sys/modules/opus rom/modules/main/opus linkfs sys/modules/opus

View File

@@ -14,7 +14,7 @@ local parentTerm = _G.device.terminal
local w,h = parentTerm.getSize() local w,h = parentTerm.getSize()
local overviewId local overviewId
local tabsDirty = false local tabsDirty = false
local closeInd = Util.getVersion() >= 1.76 and '\215' or '*' local closeInd = Util.supportsExtChars() and '\215' or '*'
local multishell = { } local multishell = { }
_ENV.multishell = multishell _ENV.multishell = multishell

View File

@@ -5,7 +5,7 @@ local cbor = require('opus.cbor')
local sha2 = require('opus.crypto.sha2') local sha2 = require('opus.crypto.sha2')
local Util = require('opus.util') local Util = require('opus.util')
local ROUNDS = 20 -- Standard ChaCha20 (was 8, upgraded for security) local ROUNDS = 8 -- Adjust this for speed tradeoff
local bxor = bit32.bxor local bxor = bit32.bxor
local band = bit32.band local band = bit32.band

View File

@@ -15,11 +15,7 @@ for _,m in pairs(methods) do
end end
function linkfs.resolve(node, dir) function linkfs.resolve(node, dir)
local mp = node.mountPoint return dir:gsub(node.mountPoint, node.source, 1)
if dir:sub(1, #mp) == mp then
return node.source .. dir:sub(#mp + 1)
end
return dir
end end
function linkfs.mount(path, source) function linkfs.mount(path, source)
@@ -45,8 +41,8 @@ function linkfs.mount(path, source)
end end
function linkfs.copy(node, s, t) function linkfs.copy(node, s, t)
s = linkfs.resolve(node, s) s = s:gsub(node.mountPoint, node.source, 1)
t = linkfs.resolve(node, t) t = t:gsub(node.mountPoint, node.source, 1)
return fs.copy(s, t) return fs.copy(s, t)
end end
@@ -54,29 +50,25 @@ function linkfs.delete(node, dir)
if dir == node.mountPoint then if dir == node.mountPoint then
fs.unmount(node.mountPoint) fs.unmount(node.mountPoint)
else else
dir = linkfs.resolve(node, dir) dir = dir:gsub(node.mountPoint, node.source, 1)
return fs.delete(dir) return fs.delete(dir)
end end
end end
function linkfs.find(node, spec) function linkfs.find(node, spec)
spec = linkfs.resolve(node, spec) spec = spec:gsub(node.mountPoint, node.source, 1)
local list = fs.find(spec) local list = fs.find(spec)
local src = node.source
local mp = node.mountPoint
for k,f in ipairs(list) do for k,f in ipairs(list) do
if f:sub(1, #src) == src then list[k] = f:gsub(node.source, node.mountPoint, 1)
list[k] = mp .. f:sub(#src + 1)
end
end end
return list return list
end end
function linkfs.move(node, s, t) function linkfs.move(node, s, t)
s = linkfs.resolve(node, s) s = s:gsub(node.mountPoint, node.source, 1)
t = linkfs.resolve(node, t) t = t:gsub(node.mountPoint, node.source, 1)
return fs.move(s, t) return fs.move(s, t)
end end

View File

@@ -35,10 +35,8 @@ end
local methods = { 'delete', 'exists', 'getFreeSpace', 'makeDir', 'list', 'listEx', 'attributes' } local methods = { 'delete', 'exists', 'getFreeSpace', 'makeDir', 'list', 'listEx', 'attributes' }
local function resolve(node, dir) local function resolve(node, dir)
local mp = node.mountPoint -- TODO: Wrong ! (does not support names with dashes)
if dir:sub(1, #mp) == mp then dir = dir:gsub(node.mountPoint, '', 1)
dir = dir:sub(#mp + 1)
end
return fs.combine(node.source, dir) return fs.combine(node.source, dir)
end end
@@ -55,7 +53,7 @@ end
function netfs.mount(_, id, source) function netfs.mount(_, id, source)
if not id or not tonumber(id) then if not id or not tonumber(id) then
error('netfs syntax: computerId [directory]') error('ramfs syntax: computerId [directory]')
end end
return { return {
id = tonumber(id), id = tonumber(id),

View File

@@ -15,7 +15,7 @@ function git.list(repository)
local user = table.remove(t, 1) local user = table.remove(t, 1)
local repo = table.remove(t, 1) local repo = table.remove(t, 1)
local branch = table.remove(t, 1) or 'main' local branch = table.remove(t, 1) or 'master'
local path local path
if not Util.empty(t) then if not Util.empty(t) then

View File

@@ -39,7 +39,8 @@ if register_global_module_table then
_G[global_module_name] = json _G[global_module_name] = json
end end
local _ENV = nil -- blocking globals in Lua 5.2 -- this was incompatible because we use fs later
--local _ENV = nil -- blocking globals in Lua 5.2
pcall (function() pcall (function()
-- Enable access to blocked metatables. -- Enable access to blocked metatables.

View File

@@ -1,38 +1,10 @@
local Config = require('opus.config') local Config = require('opus.config')
local SHA = require('opus.crypto.sha2')
local Util = require('opus.util')
local PBKDF2_ITERATIONS = 100
local Security = { } local Security = { }
local function generateSalt()
local salt = { }
for _ = 1, 16 do
salt[#salt + 1] = math.random(0, 0xFF)
end
return setmetatable(salt, Util.byteArrayMT):toHex()
end
function Security.verifyPassword(password) function Security.verifyPassword(password)
local stored = Security.getPassword() local current = Security.getPassword()
if not stored then return current and password == current
return false
end
-- New format: { hash = hex, salt = hex, iter = N }
if type(stored) == 'table' and stored.hash and stored.salt then
local iter = stored.iter or PBKDF2_ITERATIONS
local derived = SHA.pbkdf2(password, Util.hexToByteArray(stored.salt), iter)
return derived:toHex() == stored.hash
end
-- Legacy format: plain SHA-256 hex string
if type(stored) == 'string' then
return SHA.compute(password) == stored
end
return false
end end
function Security.hasPassword() function Security.hasPassword()
@@ -56,15 +28,8 @@ function Security.getIdentifier()
end end
function Security.updatePassword(password) function Security.updatePassword(password)
local salt = generateSalt()
local derived = SHA.pbkdf2(password, Util.hexToByteArray(salt), PBKDF2_ITERATIONS)
local config = Config.load('os') local config = Config.load('os')
config.password = { config.password = password
hash = derived:toHex(),
salt = salt,
iter = PBKDF2_ITERATIONS,
}
Config.update('os', config) Config.update('os', config)
end end

View File

@@ -44,7 +44,7 @@ function UI:init()
tertiary = colors.gray, tertiary = colors.gray,
} }
} }
self.extChars = Util.getVersion() >= 1.76 self.extChars = Util.supportsExtChars()
local function keyFunction(event, code, held) local function keyFunction(event, code, held)
local ie = Input:translate(event, code, held) local ie = Input:translate(event, code, held)
@@ -115,16 +115,12 @@ function UI:init()
local ie = Input:translate('mouse_up', button, x, y) local ie = Input:translate('mouse_up', button, x, y)
local currentPage = self:getActivePage() local currentPage = self:getActivePage()
if ie.code == 'control-shift-mouse_click' then -- debug inspector if ie.code == 'control-shift-mouse_click' then -- hack
local Config = require('opus.config') local event = currentPage:pointToChild(x, y)
local debugCfg = Config.load('os', { debug_inspector = false }) _ENV.multishell.openTab(_ENV, {
if debugCfg.debug_inspector then path = 'sys/apps/Lua.lua',
local event = currentPage:pointToChild(x, y) args = { event.element, self, _ENV },
_ENV.multishell.openTab(_ENV, { focused = true })
path = 'sys/apps/Lua.lua',
args = { event.element, self, _ENV },
focused = true })
end
elseif ie and currentPage and currentPage.parent.device.side == side then elseif ie and currentPage and currentPage.parent.device.side == side then
self:click(currentPage, ie) self:click(currentPage, ie)

View File

@@ -170,16 +170,19 @@ function Util.print(pattern, ...)
end end
function Util.getVersion() function Util.getVersion()
local version local versionString = _G._HOST or _G._CC_VERSION
local versionMajor, versionMinor = versionString:match("(%d+)%.(%d+)")
-- ex.: 1.89 would return 1, 89
return tonumber(versionMajor), tonumber(versionMinor)
end
if _G._CC_VERSION then function Util.compareVersion(major, minor)
version = tonumber(_G._CC_VERSION:match('[%d]+%.?[%d][%d]')) local currentMajor, currentMinor = Util.getVersion()
end return currentMajor > major or currentMajor == major and currentMinor >= minor
if not version and _G._HOST then end
version = tonumber(_G._HOST:match('[%d]+%.?[%d][%d]'))
end
return version or 1.7 function Util.supportsExtChars()
return Util.compareVersion(1, 76)
end end
function Util.getMinecraftVersion() function Util.getMinecraftVersion()