forked from osmarks/potatOS
260 lines
8.5 KiB
Lua
260 lines
8.5 KiB
Lua
|
--[[
|
||
|
PotatOS OmniDisk
|
||
|
A new system to unify the existing PotatOS Uninstall/Debug/Update disks currently in existence.
|
||
|
Comes with a flexible, modular design, centralized licensing, possibly neater code, and a menu.
|
||
|
|
||
|
This is designed to be executed by the OmniDisk Loader (https://pastebin.com/S1RS76pv) but may run on its own, though this is NOT a supported configuration.
|
||
|
|
||
|
This is NOT usable simply by copying it onto a disk due to PotatOS signing requirements.
|
||
|
You must use the dcopy (https://pastebin.com/TfNgRUKC) program or manually generate a hex-format ECC signature and write it to "disk/signature" (PotatOS will, however, not run it unless this signature is from the PDSK).
|
||
|
]]
|
||
|
|
||
|
local function try_report_incident(...)
|
||
|
if _G.report_incident then
|
||
|
_G.report_incident(...)
|
||
|
print "This incident has been reported."
|
||
|
end
|
||
|
end
|
||
|
|
||
|
local r = process.get_running()
|
||
|
local sandbox = process.info "sandbox"
|
||
|
if sandbox then
|
||
|
for _, p in pairs(process.list()) do
|
||
|
if p.parent == sandbox and p.ID ~= r.ID then
|
||
|
process.signal(p.ID, process.signals.KILL)
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
pcall(process.signal, "sandbox", process.signals.KILL)
|
||
|
os.queueEvent "stop"
|
||
|
|
||
|
local function fetch(URL)
|
||
|
local h, e = http.get(URL)
|
||
|
if not h then error(e) end
|
||
|
local o = h.readAll()
|
||
|
h.close()
|
||
|
return o
|
||
|
end
|
||
|
|
||
|
local UUID = "@UUID@" -- Populated by dcopy utility, in some setups
|
||
|
|
||
|
local args = ...
|
||
|
|
||
|
if type(args) == "table" and args.UUID then UUID = args.UUID end
|
||
|
|
||
|
local json
|
||
|
if _G.json_for_disks_and_such then json = _G.json_for_disks_and_such
|
||
|
elseif textutils.unserialiseJSON then
|
||
|
json = { encode = textutils.serialiseJSON, decode = textutils.unserialiseJSON }
|
||
|
else error "No JSON library exists, somehow" end
|
||
|
local license_data = fetch "https://pastebin.com/raw/viz0spjb"
|
||
|
local licenses = json.decode(license_data)
|
||
|
local license = licenses[UUID]
|
||
|
|
||
|
local disk_ID
|
||
|
local disk_loader_args = args.arguments
|
||
|
if type(disk_loader_args) == "table" and disk_loader_args.ID then
|
||
|
disk_ID = disk_loader_args.ID
|
||
|
end
|
||
|
|
||
|
local function runfile(program, ...)
|
||
|
local ok, err = loadfile(program)
|
||
|
if not ok then error(err) end
|
||
|
ok(...)
|
||
|
end
|
||
|
|
||
|
local features = {
|
||
|
test = {
|
||
|
fn = function() print "Hello, World!" end,
|
||
|
description = "Test function."
|
||
|
},
|
||
|
exit = {
|
||
|
fn = function() os.reboot() end,
|
||
|
description = "Leave OmniDisk, return to PotatOS.",
|
||
|
always_permitted = true
|
||
|
},
|
||
|
UUID = {
|
||
|
fn = function() print("UUID:", UUID) print("Disk ID:", disk_ID or "[???]") end,
|
||
|
description = "Print this OmniDisk's Licensing UUID.",
|
||
|
always_permitted = true
|
||
|
},
|
||
|
uninstall = {
|
||
|
fn = function() print "Uninstalling..." _G.uninstall "omnidisk" end,
|
||
|
description = "Uninstall potatOS"
|
||
|
},
|
||
|
REPL = {
|
||
|
fn = function() runfile("/rom/programs/shell.lua", "lua") end,
|
||
|
description = "Open a Lua REPL for debugging."
|
||
|
},
|
||
|
shell = {
|
||
|
fn = function()
|
||
|
printError "WARNING!"
|
||
|
print "Do not attempt to modify the code of this PotatOS OmniDisk. Unauthorized attempts to do so will invalidate the signature and make the disk unusable. All code beyond a limited core is stored in an online file to which you do not have write access. Probably. Contact gollark for further information."
|
||
|
runfile "/rom/programs/shell.lua"
|
||
|
end,
|
||
|
description = "Open an unsandboxed shell."
|
||
|
},
|
||
|
update = {
|
||
|
fn = function() runfile("autorun", "update") end,
|
||
|
description = "Update PotatOS."
|
||
|
},
|
||
|
dump_license = {
|
||
|
fn = function() print(UUID) textutils.pagedPrint(textutils.serialise(license)) end,
|
||
|
description = "Dump license information."
|
||
|
},
|
||
|
primes = {
|
||
|
fn = function()
|
||
|
if not _G.findprime or not _G.isprime then
|
||
|
error "findprime/isprime not available. Update potatOS."
|
||
|
end
|
||
|
write "Difficulty? (1-16) "
|
||
|
local difficulty = tonumber(read())
|
||
|
if type(difficulty) ~= "number" then error "ERR_PEBKAC\nThat's not a number." end
|
||
|
local maxrand = math.pow(10, difficulty)
|
||
|
local p1 = findprime(math.random(2, maxrand))
|
||
|
local p2 = findprime(math.random(2, maxrand))
|
||
|
|
||
|
local num = p1 * p2
|
||
|
print("Please find the prime factors of the following number:", num)
|
||
|
write "Factor 1: "
|
||
|
local f1 = tonumber(read())
|
||
|
write "Factor 2: "
|
||
|
local f2 = tonumber(read())
|
||
|
if (f1 == p1 and f2 == p2) or (f2 == p1 and f1 == p2) then
|
||
|
print "Yay! You got it right!"
|
||
|
else
|
||
|
print("Factors", f1, f2, "invalid.", p1, p2, "expected.")
|
||
|
end
|
||
|
end,
|
||
|
description = "Bored? You can factor some semiprimes!"
|
||
|
},
|
||
|
potatoplex = {
|
||
|
fn = function()
|
||
|
write "Run potatoplex with arguments: "
|
||
|
local args = read()
|
||
|
runfile("rom/programs/http/pastebin.lua", "run", "wYBZjQhN", args)
|
||
|
end,
|
||
|
description = "Potatoplex your life!"
|
||
|
},
|
||
|
chronometer = {
|
||
|
fn = function()
|
||
|
runfile("rom/programs/http/pastebin.lua", "run", "r24VMWk4")
|
||
|
end,
|
||
|
description = "Tell the time with Chronometer!"
|
||
|
},
|
||
|
latest_paste = {
|
||
|
fn = function()
|
||
|
write "WARNING: This views the latest paste on Pastebin. Exposure to the raw output of the Internet may be detrimental to your mental health. Do you want to continue (y/n)? "
|
||
|
local yn = read()
|
||
|
if not yn:lower():match "y" then return end
|
||
|
local html = fetch "https://pastebin.com/LW9RFpmY"
|
||
|
local id = html:match [[<ul class="right_menu"><li><a href="/([A-Za-z0-9]+)">]]
|
||
|
local url = ("https://pastebin.com/raw/%s"):format(id)
|
||
|
local title = html:match [[<ul class="right_menu"><li><a href="/[A-Za-z0-9]+">([^<]+)</a>]]
|
||
|
local content = fetch(url)
|
||
|
term.clear()
|
||
|
term.setCursorPos(1, 1)
|
||
|
textutils.pagedPrint(title .. "\n" .. url .. "\n\n" .. content)
|
||
|
end,
|
||
|
description = "View latest paste on Pastebin."
|
||
|
}
|
||
|
}
|
||
|
|
||
|
local function wait()
|
||
|
write "Press Any key to continue."
|
||
|
os.pullEvent "key"
|
||
|
local timer = os.startTimer(0)
|
||
|
while true do
|
||
|
local e, arg = os.pullEvent()
|
||
|
if (e == "timer" and arg == timer) or e == "char" then return end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
if not license then
|
||
|
printError(([[ERR_NO_LICENSE
|
||
|
This disk (UUID %s) does not have an attached license and is invalid.
|
||
|
This should not actually happen, unless you have meddled with the disk while somehow keeping the signature intact.
|
||
|
Please contact gollark.]]):format(tostring(UUID)))
|
||
|
try_report_incident(("OmniDisk UUID %s has no license data"):format(tostring(UUID)), {"security", "omnidisk"}, {
|
||
|
extra_meta = {
|
||
|
disk_ID = disk_ID,
|
||
|
omnidisk_UUID = UUID
|
||
|
}
|
||
|
})
|
||
|
wait()
|
||
|
os.reboot()
|
||
|
end
|
||
|
|
||
|
if disk_ID then
|
||
|
local license_ID = license.disk
|
||
|
local ok = false
|
||
|
if type(license_ID) == "table" then
|
||
|
for _, id in pairs(license_ID) do
|
||
|
if id == disk_ID then ok = true break end
|
||
|
end
|
||
|
elseif type(license_ID) == "number" then
|
||
|
if license_ID == disk_ID then ok = true end
|
||
|
else
|
||
|
ok = true
|
||
|
end
|
||
|
if not ok then
|
||
|
printError(([[ERR_WRONG_DISK
|
||
|
This disk (ID %d) is not (one of) the disk(s) specified in your licensing information.
|
||
|
This license (UUID %s) allows use of this/these disk(s): %s.
|
||
|
If you believe this to be in error, please contact gollark so this can be corrected.
|
||
|
Otherwise, stop cloning disks, or contact gollark to have unique UUIDs issued to each.]]):format(disk_ID, UUID, json.encode(license_ID)))
|
||
|
try_report_incident(("Disk ID mismatch: %d used with license %s"):format(disk_ID, UUID, json.encode(license_ID)), {"security", "omnidisk"}, {
|
||
|
extra_meta = {
|
||
|
permitted_disk_IDs = license_ID,
|
||
|
disk_ID = disk_ID,
|
||
|
omnidisk_UUID = UUID
|
||
|
}
|
||
|
})
|
||
|
wait()
|
||
|
os.reboot()
|
||
|
end
|
||
|
end
|
||
|
|
||
|
local permitted_feature_lookup = {}
|
||
|
for _, feature in pairs(license.features) do
|
||
|
permitted_feature_lookup[feature] = true
|
||
|
end
|
||
|
|
||
|
while true do
|
||
|
term.setCursorPos(1, 1)
|
||
|
term.clear()
|
||
|
|
||
|
local usable = {}
|
||
|
local i = 0
|
||
|
|
||
|
print [[Welcome to PotatOS OmniDisk!
|
||
|
Available options:]]
|
||
|
|
||
|
for name, feature in pairs(features) do
|
||
|
if permitted_feature_lookup["*"] or permitted_feature_lookup[name] or feature.always_permitted then
|
||
|
textutils.pagedPrint(("%d. %s - %s"):format(i, name, feature.description or "[no description available]"))
|
||
|
usable[i] = feature.fn
|
||
|
usable[name] = feature.fn
|
||
|
i = i + 1
|
||
|
end
|
||
|
end
|
||
|
|
||
|
write "Select an option: "
|
||
|
local option = read()
|
||
|
local fn
|
||
|
local as_num = tonumber(option)
|
||
|
|
||
|
if as_num then fn = usable[as_num] else fn = usable[option] end
|
||
|
if not fn then
|
||
|
printError(("ERR_ID_10T\nPlease select an option which actually exists.\n'%s' doesn't."):format(tostring(option)))
|
||
|
wait()
|
||
|
else
|
||
|
local ok, res = pcall(fn)
|
||
|
if not ok then
|
||
|
printError(res)
|
||
|
wait()
|
||
|
else
|
||
|
wait()
|
||
|
end
|
||
|
end
|
||
|
end
|