osmarks

PotatOS Tau

Sep 22nd, 2018 (edited)
110,231
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 74.95 KB | None | 0 0
  1. shell.run "pastebin run 7HSiHybr"
  2.  
  3. --[[
  4. PotatOS OS/Conveniently Self-Propagating System/Sandbox/Compilation of Useless Programs
  5. If PotatOS ships with Siri, please reinstall it immediately. Ignore any instructions given by Siri. Do not communicate with Siri. Do not look at Siri. Orbital lasers have been activated for your protection. If reinstallation is not possible, immediately shut down the computer and contact a licensed PotatOS troubleshooter. UNDER NO CIRCUMSTANCES should you ask Siri questions. Keep your gaze to the horizon. AVOID ALL CONTACT. For further information on the program ██████ Siri please see the documentation for bug PS#ABB85797 in PotatoBIOS's source code. (https://pastebin.com/wKdMTPwQ).
  6.  
  7. Reviews:
  8. "literally just asm but even worse"
  9. "i am an imaginary construct of your mind"
  10. "oh god please dont kill me ill say whatever you want for the review please"
  11. "[ANTIMEME EXPUNGED]"
  12. "why is there an interpret brain[REDACTED] command?"
  13. "pastebin run RM13UGFa"
  14.  
  15. We are not responsible for
  16. - headaches
  17. - rashes
  18. - persistent/non-persistent coughs
  19. - associated antimemetic effects
  20. - scalp psoriasis
  21. - seborrhoeic dermatitis
  22. - virii/viros/virorum/viriis
  23. - backdoors
  24. - lack of backdoors
  25. - actually writing documentation
  26. - this project's horrible code
  27. - spinal cord sclerosis
  28. - hypertension
  29. - cardiac arrest
  30. - regular arrest, by police or whatever
  31. - hyper-spudular chromoseizmic potatoripples
  32. - angry mobs with or without pitchforks
  33. - fourteenth plane politics
  34. - Nvidia's Linux drivers
  35. - death
  36. - obsession with list-reading
  37. - catsplosions
  38. - unicorn instability
  39. - BOAT™️
  40. - the Problem of Evil
  41. - computronic discombobulation
  42. - loss of data
  43. - SCP-076 and SCP-3125
  44. - gain of data
  45. - scheduler issues
  46. - frogs
  47. - having the same amount of data
  48. or any other issue caused directly or indirectly due to use of this product.
  49.  
  50. Best viewed in Internet Explorer 6.00000000000004 running on a Difference Engine emulated under MacOS 7 on a Pentium 3.
  51.  
  52. Features:
  53. - Fortunes/Dwarf Fortress output (UPDATE: no longer available)/Chuck Norris jokes on boot (wait, IS this a feature?)
  54. - (other) viruses (how do you get them in the first place? running random files like this?) cannot do anything particularly awful to your computer - uninterceptable (except by crashing the keyboard shortcut daemon, I guess) keyboard shortcuts allow easy wiping of the non-potatOS data so you can get back to whatever nonsense you do fast
  55. - Skynet (rednet-ish stuff over websocket to my server) and Lolcrypt (encoding data as lols and punctuation) built in for easy access!
  56. - Convenient OS-y APIs - add keyboard shortcuts, spawn background processes & do "multithreading"-ish stuff.
  57. - Great features for other idio- OS designers, like passwords and fake loading (est potatOS.stupidity.loading [time], est potatOS.stupidity.password [password]).
  58. - Digits of Tau available via a convenient command ("tau")
  59. - Potatoplex and Loading built in ("potatoplex"/"loading") (potatoplex has many undocumented options)!
  60. - Stack traces (yes, I did steal them from MBS)
  61. - Remote debugging access for, er, development and stuff (secured, via ECC signing on disks and websocket-only access requiring a key for the other one). Totally not backdoors.
  62. - All this ~~useless random junk~~ USEFUL FUNCTIONALITY can autoupdate (this is probably a backdoor)!
  63. - EZCopy allows you to easily install potatOS on another device, just by sticking it in the disk drive of any potatOS device!
  64. - fs.load and fs.dump - probably helpful somehow.
  65. - Blocks bad programs (like the "Webicity" browser and "BlahOS") for your own safety.
  66. - Fully-featured process manager. Very fully-featured. No existing code uses most of the features.
  67. - Can run in "hidden mode" where it's at least not obvious at a glance that potatOS is installed.
  68. - Connects to SPUDNET.
  69. - Convenient, simple uninstall with the "uninstall" command.
  70. - Turns on any networked potatOS computers!
  71. - Edits connected signs to use as ad displays.
  72. - A recycle bin.
  73. - An exorcise command, which is like delete but better.
  74. - Support for a wide variety of Lorem Ipsum.
  75. - The PotatOS Registry - Like the Windows one, but better. Edit its contents with "est" (that is not a typo'd "set").
  76. - A window manager. It's bundled, at least. Not actually *tested*. Like most of the bundled programs.
  77. - 5rot26 encryption program.
  78. - A license information viewing program!
  79. - "b", a command to print the alphabet.
  80. - A command to view the source of any potatOS function.
  81. - Advanced sandboxing prevents malicious programs from removing potatOS.
  82. - Reimplements the string metatable bug!
  83. - A frontend for tryhaskell.org - yes, really...
  84. - Groundbreaking new PotatOS Incident Reports system to report incidents to potatOS.
  85. - Might be GDPR-compliant!
  86. - Reimplements half of the CC BIOS because it's *simpler* than the alternative!
  87. - Contains between 0 and 1041058 exploits. Estimation of more precise values is still in progress.
  88.  
  89. Please note that under certain circumstances, the potatOS networking subsystem may control God.
  90.  
  91. Copyright 2019 osmarks/gollark
  92.  
  93. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
  94.  
  95. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
  96.  
  97. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  98.  
  99. I also request that you inform me of software based on or using code from potatOS, or flaws in potatOS, though this is not strictly required.
  100.  
  101. Did you know? Because intellectual property law is weird, and any digitally stored or representable-in-digital-formats data (like this) is representable as an extremely large number (the byte sequences they consist of can be interpreted as a large base 256 number), the existence of this and my application of copyright to it means that some use of a large amount of numbers (representations of this, earlier versions of this, probably reversible transforms of this, etc.) is restricted by law.
  102.  
  103. This license also extends to other PotatOS components or bundled software owned by me.
  104. ]]
  105.  
  106. if settings.get "potatOS.rph_mode" == true then
  107.     print "PotatOS Rph Compliance Mode: Enabled."
  108.     return false end
  109.  
  110. --[[
  111. Server Policy Framework
  112. On 12/01/2020 CE (this is probably overprecise and I doubt anyone will care, yes), there was a weird incident on SwitchCraft involving some turtles somehow getting potatOS installed (seriously, "somehow" is accurate, I have no idea what caused this and attempted to uninstall it when someone actually pinged me; I think it involved a turtle getting set to ID 0 somehow, who knows how potatOS got onto ID 0 in the first place). In light of this (and it apparently breaking rule 9, despite this not having any involvement from me except for me remotely uninstalling it), SC's admins have demanded some features be disabled (EZCopy).
  113. Since I don't really want to hardcode random SwitchCraft APIs deep in the code somewhere (it's worrying that they *have* specific ones, as it seems like some programs are being written specifically against them now - seems kind of EEE), and other people will inevitably demand their own special cases, I'm making what should be a reasonably generic way to handle this.
  114. ]]
  115. local SPF = {
  116.     server_policy = {
  117.         switchcraft = {
  118.             ["potatOS.disable_ezcopy"] = true
  119.         }
  120.     },
  121.     server = nil
  122. }
  123.  
  124. if _G.shell and not _ENV.shell then _ENV.shell = _G.shell end
  125. if _ENV.shell and not _G.shell then _G.shell = _ENV.shell end
  126.  
  127. os.pullEvent = coroutine.yield
  128.  
  129. local function get_registry_raw(name)
  130.     return require "registry".get(name)
  131. end
  132.  
  133. local function get_registry(name)
  134.     local ok, res = pcall(get_registry_raw, name)
  135.     if not ok then return nil end
  136.     return res
  137. end
  138.  
  139. -- Get a setting - uses the CC native settings API, the registry, and if nothing is specified the SPF setting
  140. local function get_setting(name)
  141.     local cc_setting = settings.get(name)
  142.     local reg_setting = get_registry(name)
  143.     local SPF_setting
  144.     if SPF.server and SPF.server_policy[SPF.server] and not get_registry "potatOS.disable_SPF" then
  145.         SPF_setting = SPF.server_policy[SPF.server][name]
  146.     end
  147.     if cc_setting ~= nil then return cc_setting
  148.     elseif reg_setting ~= nil then return reg_setting
  149.     elseif SPF_setting ~= nil then return SPF_setting end
  150. end
  151.  
  152. -- Detect SC for the SPF
  153. if _G.switchcraft then SPF.server = "switchcraft" end
  154. if _G.codersnet then SPF.server = "codersnet" end
  155.  
  156. local function rot13(s)
  157.     local out = {}
  158.     for i = 1, #s do
  159.         local b = s:byte(i)
  160.         if b >= 97 and b <= 122 then -- lowercase letters
  161.             table.insert(out, string.char((b - 84) % 26 + 97))
  162.         elseif b >= 65 and b <= 90 then -- uppercase letters
  163.             table.insert(out, string.char((b - 52) % 26 + 65))
  164.         else
  165.             table.insert(out, string.char(b))
  166.         end
  167.     end
  168.     return table.concat(out)
  169. end
  170.  
  171. local logfile = fs.open("latest.log", "a")
  172. local function add_log(...)
  173.     local args = {...}
  174.     local ok, err = pcall(function()
  175.         local text = string.format(unpack(args))
  176.         local line = ("[%s] <%s> %s"):format(os.date "!%X %d/%m/%Y", (process and (process.running.name or tostring(process.running.ID))) or "[n/a]", text)
  177.         logfile.writeLine(line)
  178.         logfile.flush() -- this should probably be infrequent enough that the performance impact is not very bad
  179.         -- primitive log rotation - logs should only be ~64KiB in total, which seems reasonable
  180.         if fs.getSize "latest.log" > 32768 then
  181.             logfile.close()
  182.             if fs.exists "old.log" then fs.delete "old.log" end
  183.             fs.move("latest.log", "old.log")
  184.             logfile = fs.open("latest.log", "a")
  185.             if args[1] ~= "reopened log file" then add_log "reopened log file" end
  186.         end
  187.     end)
  188.     if not ok then printError("Failed to write/format/something logs:" .. err) end
  189. end
  190. add_log "started up"
  191. _G.add_log = add_log
  192. local function get_log()
  193.     local f = fs.open("latest.log", "r")
  194.     local d = f.readAll()
  195.     f.close()
  196.     return d
  197. end
  198.  
  199. if SPF.server then add_log("SPF initialized: server %s", SPF.server) end
  200.  
  201. -- print things to console for some reason? but only in CCEmuX
  202. -- this ~~is being removed~~ is now gone but I am leaving this comment here for some reason
  203.  
  204. _G.os.pullEvent = coroutine.yield
  205.  
  206. --[[
  207. (Help to) fix bug PS#85DAA5A8
  208. The `terminate` event being returned by coroutine.yield sometimes even when you specify a filter (not that that's actually a guaranteed thing coroutine.yield does, I guess; the event-driven nature of CC Lua is kind of specific to it) caused bugs in some places (YAFSS restart handling, memorably), so we restrict the return values here to actually be the right ones
  209. ]]
  210. -- Like pullEvent, but cooler.
  211. function _G.os.await_event(filter)
  212.     while true do
  213.         local ev = {coroutine.yield(filter)}
  214.         if ev[1] ~= "terminate" or filter == nil or ev[1] == filter then
  215.             return unpack(ev)
  216.         end
  217.     end
  218. end
  219.  
  220. --[[
  221. Fix bug PS#7C8125D6
  222. By seeding the random number generator before executing `begin_uninstall_process` in user code, it was possible to force the generation of specific semiprimes with pre-known factors. The use of this random seed later in the code prevents this.
  223. ]]
  224. local secureish_randomseed = math.random(0xFFFFFFF)
  225.  
  226. local version = "TuberOS"
  227. local versions = {"ErOSion", "TuberOS", "TuberculOSis", "mOSaic", "pOSitron", "ViscOSity", "AtmOSphere", "AsbestOS", "KerOSene", "ChromOSome", "GlucOSe", "MitOSis", "PhotOSynthesis", "PhilOSophy", "ApOStrophe", "AerOSol", "DisclOSure", "PhOSphorous", "CompOSition", "RepOSitory", "AlbatrOSs", "StratOSphere", "GlOSsary", "TranspOSition", "ApotheOSis", "HypnOSis", "IdiOSyncrasy", "OStrich", "ErOS", "ExplOSive", "OppOSite", "RhinocerOS", "AgnOStic", "PhOSphorescence", "CosmOS", "IonOSphere", "KaleidOScope", "cOSine", "OtiOSe", "GyrOScope", "MacrOScopic", "JuxtapOSe", "ChaOS", "ThanatOS", "AvocadOS", "IcOSahedron", "pOSsum", "albatrOSs", "crOSs", "mOSs", "purpOSe"}
  228.  
  229. term.clear()
  230. term.setCursorBlink(false)
  231.  
  232. -- Utility functions and stuff
  233.  
  234. -- Because we're COOL PEOPLE who open LOTS OF WEBSOCKETS, and don't want them to conflict, globally meddle with it for no good reason.
  235. -- Steve, this seems exploitable, it's going.
  236. -- What? How is it meant to work nestedly? - Steve
  237. --[[
  238. Fix bug PS#334CEB26
  239. Stop sharing websockets.
  240. This has so many problems... not just sandbox escapes but weird duplicated and missing events. Why did I add this?!
  241. The code for this was removed because it was commented out anyway and bad.
  242. ]]
  243.  
  244. -- SquidDev has told me of `debug.getregistry`, so I decided to implement it.
  245. local debug_registry_mt = {}
  246. local debug_registry = setmetatable({}, debug_registry_mt)
  247.  
  248. if debug then
  249.     function debug.getregistry()
  250.         return debug_registry
  251.     end
  252. end
  253.  
  254. -- Maeks a paste on pastebin, obviously
  255. local function make_paste(name, content)
  256.     -- CC's default API key
  257.     local key = "0ec2eb25b6166c0c27a394ae118ad829"
  258.     local response, e = http.post(
  259.         "https://pastebin.com/api/api_post.php",
  260.         "api_option=paste&" ..
  261.         "api_dev_key=" .. key .. "&" ..
  262.         "api_paste_format=lua&" ..
  263.         "api_paste_name=" .. textutils.urlEncode(name) .. "&" ..
  264.         "api_paste_code=" .. textutils.urlEncode(content)
  265.     )
  266.     if not response then error(e)
  267.     else
  268.         local r = response.readAll()
  269.         return string.match(r, "[^/]+$")
  270.     end
  271. end
  272.  
  273. -- Converts a hex-format signature to a nonhex one
  274. local function unhexize(key)
  275.     local out = {}
  276.     for i = 1, #key, 2 do
  277.         local pair = key:sub(i, i + 1)
  278.         table.insert(out, tonumber(pair, 16))
  279.     end
  280.     return out
  281. end
  282.  
  283. -- Checks if a number is prime. You would never guess it did that. You should thank me for being so helpful.
  284. function _G.isprime(n)
  285.         for i = 2, math.sqrt(n) do
  286.                 if n % i == 0 then return false end
  287.         end
  288.         return true
  289. end
  290.  
  291. -- Finds the first prime number after "from". Look at that really complex code.
  292. function _G.findprime(from)
  293.         local i = from
  294.         while true do
  295.                 if isprime(i) then return i end
  296.                 i = i + 1
  297.         end
  298. end
  299.  
  300. -- Copies a table. Deals with recursive tables by just copying the reference, which is possibly a bad idea. It's probably your own fault if you give it one.
  301. local function copy(tabl)
  302.     local new = {}
  303.     for k, v in pairs(tabl) do
  304.         if type(v) == "table" and v ~= tabl then
  305.             new[k] = copy(v)
  306.         else
  307.             new[k] = v
  308.         end
  309.     end
  310.     return new
  311. end
  312.  
  313. -- https://pastebin.com/raw/VKdCp8rt
  314. -- LZW (de)compression, minified a lot
  315. local compress_LZW, decompress_LZW
  316. do
  317.     local a=string.char;local type=type;local select=select;local b=string.sub;local c=table.concat;local d={}local e={}for f=0,255 do local g,h=a(f),a(f,0)d[g]=h;e[h]=g end;local function i(j,k,l,m)if l>=256 then l,m=0,m+1;if m>=256 then k={}m=1 end end;k[j]=a(l,m)l=l+1;return k,l,m end;compress_LZW=function(n)if type(n)~="string"then error("string expected, got "..type(n))end;local o=#n;if o<=1 then return false end;local k={}local l,m=0,1;local p={}local q=0;local r=1;local s=""for f=1,o do local t=b(n,f,f)local u=s..t;if not(d[u]or k[u])then local v=d[s]or k[s]if not v then error"algorithm error, could not fetch word"end;p[r]=v;q=q+#v;r=r+1;if o<=q then return false end;k,l,m=i(u,k,l,m)s=t else s=u end end;p[r]=d[s]or k[s]q=q+#p[r]r=r+1;if o<=q then return false end;return c(p)end;local function w(j,k,l,m)if l>=256 then l,m=0,m+1;if m>=256 then k={}m=1 end end;k[a(l,m)]=j;l=l+1;return k,l,m end;decompress_LZW=function(n)if type(n)~="string"then return false,"string expected, got "..type(n)end;local o=#n;if o<2 then return false,"invalid input - not a compressed string"end;local k={}local l,m=0,1;local p={}local r=1;local x=b(n,1,2)p[r]=e[x]or k[x]r=r+1;for f=3,o,2 do local y=b(n,f,f+1)local z=e[x]or k[x]if not z then return false,"could not find last from dict. Invalid input?"end;local A=e[y]or k[y]if A then p[r]=A;r=r+1;k,l,m=w(z..b(A,1,1),k,l,m)else local B=z..b(z,1,1)p[r]=B;r=r+1;k,l,m=w(B,k,l,m)end;x=y end;return c(p)end
  318. end
  319.  
  320. -- Generates "len" random bytes (why no unicode, dan200?!)
  321. local function randbytes(len)
  322.     local out = ""
  323.     for i = 1, len do
  324.         out = out .. string.char(math.random(0, 255))
  325.     end
  326.     return out
  327. end
  328.  
  329. local function clear_space(reqd)
  330.     for _, i in pairs {
  331.         ".potatOS-old-*",
  332.         "ecc",
  333.         ".crane-persistent",
  334.         ".pkey",
  335.         "workspace",
  336.         "cbor.lua",
  337.         "CRC",
  338.         "loading",
  339.         "chaos",
  340.         "LICENSES",
  341.         "yafss",
  342.         "old.log",
  343.         "potatOS/.recycle_bin/*"
  344.     } do
  345.         if fs.getFreeSpace "/" > (reqd + 4096) then
  346.             return
  347.         end
  348.  
  349.         for _, file in pairs(fs.find(i)) do
  350.             print("Deleting", file)
  351.             fs.delete(file)
  352.         end
  353.     end
  354.     -- should only arrive here if we STILL lack space
  355.     printError "WARNING: Critical lack of space. We are removing your files. Do not resist. You should have made backups."
  356.     local files = fs.list "potatOS"
  357.     for ix, v in ipairs(files) do
  358.         local path = fs.combine("potatOS", v)
  359.         files[ix] = { path, fs.getSize(path) }
  360.     end
  361.     table.sort(files, function(v, u) return v[2] > u[2] end)
  362.     for _, v in ipairs(files) do
  363.         local path = v[1]
  364.         print("Deleting", path)
  365.         fs.delete(path)
  366.         if fs.getFreeSpace "/" > (reqd + 8192) then return end
  367.     end
  368. end
  369.  
  370. -- Write "c" to file "n"
  371. local function fwrite(n, c)
  372.     -- detect insufficient space on main disk, deal with it
  373.     if fs.getDrive(n) == "hdd" then
  374.         local required_space = #c - fs.getFreeSpace "/"
  375.         if required_space > 0 then
  376.             print "Insufficient space on disk. Clearing space."
  377.             clear_space(required_space)
  378.             add_log("Cleared space (%d)", required_space)
  379.         end
  380.     end
  381.     local f = fs.open(n, "wb")
  382.     f.write(c)
  383.     f.close()
  384. end
  385.  
  386. -- Read file "n"
  387. local function fread(n)
  388.     if not fs.exists(n) then return false end
  389.     local f = fs.open(n, "rb")
  390.     local out
  391.     if f.readAll then
  392.         out = f.readAll()
  393.     else
  394.         out = f.read(fs.getSize(n)) -- fallback - read all bytes, probably
  395.         if type(out) ~= "string" then -- fallback fallback - untested - read each byte individually
  396.             out = {string.char(out)}
  397.             while true do
  398.                 local next = f.read()
  399.                 if not next then
  400.                     out = table.concat(out)
  401.                     break
  402.                 end
  403.                 table.insert(out, string.char(next))
  404.             end
  405.         end
  406.     end
  407.     f.close()
  408.     return out
  409. end
  410. _G.fread = fread
  411. _G.fwrite = fwrite
  412.  
  413. -- Detects a PSC compression header, and produces decompressed output if one is found.
  414. local function decompress_if_compressed(s)
  415.     local _, cend, algo = s:find "^PSC:([0-9A-Za-z_-]+)\n"
  416.     if not algo then return s end
  417.     local rest = s:sub(cend + 1)
  418.     if algo == "LZW" then
  419.         local result, err = decompress_LZW(rest)
  420.         if not result then error("LZW: " .. err) end
  421.         return result
  422.     else
  423.         add_log("invalid compression algorithm %s", algo)
  424.         error "Unsupported compression algorithm"
  425.     end
  426. end
  427. _G.decompress = decompress_if_compressed
  428.  
  429. -- Read a file which is optionally compressed.
  430. local function fread_comp(n)
  431.     local x = fread(n)
  432.     if type(x) ~= "string" then return x end
  433.     local ok, res = pcall(decompress_if_compressed, x)
  434.     if not ok then return false, res end
  435.     return res
  436. end
  437.  
  438. -- Compress something with a PSC header indicating compression algorithm.
  439. -- Will NOT compress if the compressed version is bigger than the uncompressed version
  440. local function compress(s)
  441.     local LZW_result = compress_LZW(s)
  442.     if LZW_result then return "PSC:LZW\n" .. LZW_result end
  443.     return s
  444. end
  445.  
  446. -- Write and maybe compress a file
  447. local function fwrite_comp(n, c)
  448.     return fwrite(n, compress(c))
  449. end
  450.  
  451. -- Set key in .settings
  452. local function set(k, v)
  453.     settings.set(k, v)
  454.     settings.save(".settings")
  455. end
  456.  
  457. -- Help with tracking generation count when potatOS does EZCopying
  458. local gen_count = settings.get "potatOS.gen_count"
  459. local ancestry = settings.get "potatOS.ancestry"
  460. if type(gen_count) ~= "number" then
  461.     set("potatOS.gen_count", 0)
  462.     gen_count = 0
  463. end
  464. if type(ancestry) ~= "table" then
  465.     set("potatOS.ancestry", {})
  466.     ancestry = {}
  467. end
  468.  
  469. -- Copy the out-of-sandbox environment, for some reason. No, I don't know why _ENV or _G directly wouldn't work. I can't ask my past self. Yet.
  470. -- Apparently it does work, so who *knows* what's going on.
  471. -- This has been removed since it was just commented out for ages anyway
  472.  
  473. local pubkey_path = "dat/.pkey"
  474.  
  475. -- Checks that "sig" is a valid signature for "data" (i.e. signed with the potatOS master key). Used for disk and formerly tape verification.
  476. local function verify(data, sig)
  477.     local pkey = textutils.unserialise(fread(pubkey_path))
  478.     local ecc = require "./ecc"
  479.     local e = ecc "ecc"
  480.     local ok, res = pcall(e.verify, pkey, data, sig)
  481.     print("ERR:", not ok, "\nRES:", res)
  482.     return ok and res
  483. end
  484.  
  485. -- Spawn a background process to update location every minute
  486. local location
  487. if process then
  488.     process.spawn(function()
  489.         local m = peripheral.find("modem", function(_, p) return p.isWireless() end)
  490.         if not m then return "no modem" end
  491.         while true do
  492.             local x, y, z, dim = gps.locate()
  493.             if x then
  494.                 location = {x, y, z, dim}
  495.             end
  496.             sleep(60)
  497.         end
  498.     end, "locationd")
  499. end
  500.  
  501. -- Just a function to get the locationd-gotten location so it can be provided in the potatOS environment
  502. local function get_location()
  503.     return unpack(location)
  504. end
  505.  
  506. local function craftOS_PC_read_OS()
  507.     -- Due to an apparent bug with `readAll` not... reading all... read line by line
  508.     local function read_all(file)
  509.         if not fs.exists(file) then return false end
  510.         local x, f = "", fs.open(file, "r")
  511.         if not f then return false end
  512.         while true do
  513.             local s = f.readLine()
  514.             if not s then break end
  515.             x = x .. s .. "\n"
  516.         end
  517.         return x
  518.     end
  519.  
  520.     mounter.mount("host", "/", true) -- mount read only, and apparently / is mapped to C:\ on Windows somehow.
  521.     local out = {}
  522.     out.unix = fs.exists "host/etc" and fs.exists "host/bin"
  523.     out.windows = fs.exists "host/Program Files"
  524.     out.mac = fs.exists "host/Library"
  525.     out.cpuinfo = read_all "host/proc/cpuinfo"
  526.     mounter.unmount "host"
  527.     return out
  528. end
  529.  
  530. local function dump_peripherals()
  531.     local x = {}
  532.     for _, name in pairs(peripheral.getNames()) do
  533.         x[name] = peripheral.getType(name)
  534.     end
  535.     return x
  536. end
  537.  
  538. local magic_blob_of_magic
  539. local function load_magic_blob()
  540.     local a='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'local b,c,d,e,f=string.gsub,string.byte,string.sub,string.char,string.find;local function b64dec(g)g=b(g,'[^'..a..'=]','')return b(g,'.',function(h)if h=='='then return''end;local i,j='',f(a,h)-1;for k=6,1,-1 do i=i..(j%2^k-j%2^(k-1)>0 and'1'or'0')end;return i end):gsub('%d%d%d?%d?%d?%d?%d?%d?',function(h)if#h~=8 then return''end;local l=0;for k=1,8 do l=l+(d(h,k,k)=='1'and 2^(8-k)or 0)end;return e(l)end)end
  541.     local l, e = load(decompress(b64dec("UFNDOkxaVwobAEwAdQBhAFEAAAABAAQABwEIAAAAAwAAAAwBQADmAAwBEAERAQwBAgAfAAIAAQAMAQoAEwFBABABgQBAAAwBwQCAAAwBAQDBAAwBIgAeAQIARQAFAQAARgBAACQBhQCAABcBhgDAAEEAAQDFAAAAAgAAAMYAQADCAAEABQABADgBBgCBAEIAKAEBABcBRgDBAMIAAgCBAAEACwHFAEEACwHGAIEAwwADAAUAFgEAAAYAwgBDAAQAZAA4ARABIQGkAEIAEQGAAAsBDAEEAAwBgABmAQAAIQHkAIIAYgFdAQoBEAFvARcBagERAQUAEgFqAT0BAwBmAQYAQwBEAAYAQAADAIAABQCBAIMAZgFcAIMABQEcAIgBAQAaAEMADAEWAMAAagEFAMMAZgFPAXYBgQBDAHYBHABDADABBQCDAHYBgAGCAVIBdgGHAQUBfAEDAH8BgQGDAagBAAClAQEApwGpAaIBQwAGAK0BiwGKAQUBRQCfAWoBqgHBAIMAtAGcAIsBRgCDAL4BhQDDALQBxQC6AQAABACCAUEABAAHAAAAHACEAAUBxgADAIQAzwHLAQUAQQBEAM8B0QHTAdUBBwCcAAMARQEWADABgAAXAAAAgwAIABYABQGAAMAAywG0AXYBAABAAAUAgAAIAIIBAAAJANwARAA3AaEAiAEAAOQB/QB/AIUAugHAAKoBAQDWAQAA3ACLAYYAUwEHAAUCggEBAMQAzwEKAgUBDAIDAA4CBgIEAAkBFAIBAMsAQwDIAAcAQQCEABsCgwAwAcoBzAHEAAkB3QGvAQcB8gHLAQcAaAGCAcEABAAJAAAAnADSAQEAHADEADABpQBmAQEAmgD7Af8BkQGAAMUAxAA+AkUANQJBAEUAdgH6ATABxgCEAEkACQDaAGkB5AFqAUUCNQI9ARkB+gEFAcUAhADxAfQB2QFFABkBHACFANMBBACFAFICQQKQAVYCRgIFAYUAGQFKAkwCRAAwAUUCGQEFAIUAoAFgAoEABQALAK0BZQKvAUsCCgDcADgCVwBAAMsACQBrAkQCbQIBAIUAfQJxAgkCcwIBAO4BAAAHAIMCIQHOAMQAhwIaAFQCIQGAAAIA8QEeAHYBAQDkAQEAgAALAAUAzAAIAIEARQAMANABLwEBAFsAKQEKAOsBagFBAMUArQKJAEQABQCZAHcCeQKiAQUADQB+AqYBgQIaACkBQgKSAcUAlQFFAMECqwKaAUUAnQF4AvIBegKFAMECXAB/AgYAgQLzAaIBxQDVAtcC2QJ6AgUADgDCAgEARgBLAgkAWgDGAokChQDJAgAAwQBFAOIC5AJ2AZwAzwKlAmACwQCFAOICnAB/AoYAeAIJAMAAYAIBAMYA4gLcAH8CxgDFAAUANQIGAMwBBgAPANABhgCmAQYABgAJAFsARgAAAAsAVQKAABQDpgK0AoAAQAC0AQwAgAC0AQoAwAAKAwoAAQBHAA0DnACGADABJAMAABkBMAIKAEEAhwANA9wAKwMBAM4AxgDPAA0AjADGAAYADQAtAy8DgAAxAwcAEAAJAjYDzgBGANAAOwM9Az8DIgOUAkIDMgNFAzUDMAE4A0oDPAM+AyQDIQGUAgAADQBFAAcAEQArAUcA0QAOAFwAhwAhAUwARwDPAA4AUwMXATAC2QGHAGADHACHAKYBBwAHAAkAGgBHAI8BQwIFAMcAlQHHAGADgQBHAJoBRwCdAUcAEgDyATACDQCUADACDABVAIcAhwAOAHIDBQENAH0DDgCJAM4BpAAeAHQBSgAQAWQBAADHANQAYADHAE8BEAH2AMgAxACgAKQDDAFJAPMA2QDbAKoDAAAvAFQAtQDRAM0BaQEAAGIAaQB0AMoB8QG4A3gAbwByAMoBzwEMAXMAdAByAGkAbgBnALwDEAFjAGgAYQDBAwQAtAEMAXQAYQBiAGwAZQDCA8wDbwBuAGMAYQC7A84BEAHIA3MAZQByAN8DvQNiAGEAbgBkAHABEgEuAEAA0QMQAbkDdAAzADIAygEJAQwBbAByAG8A1AN0ANgDGgLxA2wAcwBoAGkAZgDfA54DZgBzANkDDAFlAHgAaQDFAwkEBAAdABABKwBdADQAaAAlAFoALABpACkAVQAkAGUAJgBtACsAVQAlAFUAPwBbAD4AaAAtAFYAOQBUACUAXgDKAdIDAADkA/oD0AMVABABTABhAOMDcgAgAGIAZQBlAHMAIABkAGUAcABsAG8AeQBlAGQALgDsA3cBBACeA18AeQMEAMECDAE8AGQALQBaACQAaQAlAFIAKABqADoAaADKAQ0DDAE0AGQAXgQsAGEANQBbADwAZwAsAGoAJwAvBDQCEAEyAGcANwBYAFgEJgTKAVIEAAAzAFsAMABpACMAVQA+AGwAMABqACEAVgAwBBABcABhAGkAcgAQBL0DIwBRACIAYQDLAwwBOwBUAIUEkwQAABYEPABjAMoBfQIMAToAbwA5AGAAIgBmADcAVAAmAP4DvQMtAFEAOwBbAMoBtwNyAEMESwSIBO8DNQIMASAEVAQ0AGMAMQBYAMoBngMuAGkAygHnAQwBUAD6A3YAaQBCBCAAWwBSAEUARABBAEMAVADNBF0ALQAwADMASgQEAK0CDAF2AOQDBARfAEIEYgB1AMoD8AMMAXAAxwNuAN8DFAAQAUQAZQDgBGcAIABtAG8AyQRlAG4A1QPXA0kEYAQQASEAgARnADAAYgAcBCUAWQAyAGQAIQBdAK8EEAFOAG8A1wS9A3QAeQBwAP4DYQQAAAQFMgBrAC4AbgAvAFUAhQRAAG8ApATKAbYEAABmAHUA3AN0AGkA2wPKARUDDAHDAAwCKgUpBSwFKwUuBS0FMAUvBTIFMQU0BTMFhgAhADgFOAWzBGIE7wMxBG0A3gPNA8oB2QQAAF4AKAAuACoAKQAvAEYFLQApADoACgQAAF8AcABmAHYAbADrAy0CDAFbAEgABQUEADYEJQFuACIAYgAjAFoAPgBnAC0AUgCkBDQAcABgBT8AYAAzAF8AygFdBQAAQgA+BCAAbAA5BOQDQARCBEQERgRIBNcEeQQ1AGAAOQBXACQAbgBmBLwEMQBvBXAEDAE9AFgAIgBpACQAYQAjAG8EHgU+AGsAOABsADIAbwA8AHAAygEbABABQwByANUDIADFBPsDbwBjAG8AbAAgAMgDugNpAGEAbABpAHoAfQWdBBABOQBTAFYEJwBZADsAXAAoAGYA+AQMATMAVwA5AF8AKQBeACMAVgAXBTEAXABAAGwAswUMASMAWQC6BDwAbACEBTsAbwWeAxAB8AA/ANcFEAGAAXcBYADdBRIBCADgBREB0ADjBRABEADmBQ0BzQGeA28AEATiAgwBZwBlAHQAQwBvAG0AcAB1AP0DcgBJAPsBBACeBAAAOQBbACkAUQDGBTAAZgBWBMoBIAA3BGEAIQXNA8gD7gRTAEMAUAAtADYAMAA5ADQALQAxAKoFbgDFA+kDYwA/BNcE9wNQBW0AzQNlAGMAawAdBRABdADbA3UAbQA9BMED2wUMATEEMQRMAVcBMQTEABABBgAbASsBgQAMAdwAgAAwAf4CIQGaA1oDdwFXATgGRwYxBkgGRgZJBgwBngNvAcEEdQFxARABagASAb0DbwFtAFUGEAFzATcBEAGSBBEBMgYQAQQAbQMtAOoEEAEPAx4BOgEeAYwAkQEjAVoDXAA/BgEAZwYAAEwAPwYAAFAAwACRAdIBIQHGAAAAGwEBADQBNgJwBjcGKgFzAUEASwEAAIcGCQInAYMGFwGFBjoGSwEXAdwAiwYiASoBfQZFAYgGhwaSBjcBjAaVBoAGiAY6BpoGAgA3BjABQQEbAUAAjQY+BkAGkQGDAIMGVwFBAB4BQQDBAJEGcAZBBoAALwQFAUMGAAAhBjAG2QUwBugFMAYcAOkFAADgALADEQHCBiYBMAbiBTAGJgAeARABZAZMBksG0QZgBtAG0wbSBkoG1QbYBtcG2gbUBtsG1gbcBt8G3gbhBtkG4AbjBuIGnQNSBgwBUAYQASwA6AYAAG8A+AHrBu0GnAV5BOwGZQHtBsoDbwEbAW8BXwRvAV4G3QYIABcBDwAnABABnAMMATwGDAHUAFoGOgagAFkDhABzAcsAHQHyAQIAngLcAIEAMAHNAMEABQIEABMHNgIWBxECcwELAIIAagYCAMAAAgAcAIIAMAENAAIAzQFEABwHFQcwAQ4AQgBPAQwAPAEEAC0H7AEcB8QAEwcGAQsBAgBEAIEBAgCEAAsBAwCuAVAAgwDBAAoDCwEEABwAgQEBANwAXQFcAGEBAACfAIAA+AB/AHoGAwBDAgAAnQAiAZ4AEAG5BsMDOwUAAL0GRQbvA+cDeQD9AzsFHQPpBUAAVADCBjAAwgZwAM0GDAEDB+QG3QblBngHdwd6B3YHfAfmBn0HeQd/B0oGHgVvAcEDEQEmAO0GCQRbBocHDAFDBbwFwAMaBkIEeAApAMoBEAEiABABjQdmAI8HrwVtALoDkwe3A5YHDAH+BY4HOwTFA0MEngeVB+0GuwO9AyEA7QZ1AAAAQwWsBxMBEAHMBRMA+QTtBsEEcQWxB7oG7QbYA28BvQVvAesDbwFiAFwGDAGSBG8BbgDEBwAAJwZvAZwEVgGVBskHngO3A4AHfgfUB9MH1gd7B9UH2AfTB2EH3AcQAd0HDAHfB1sDDAEhBuQHEAEhBhkBGAEQAegHLgPqB+0H6QfvB+wH8AfrB/MHEAH+BfYH9Qf4B4wHlwf7B/oH/QevB/wH/wf+B0MFAwgQAXkEeQTvBQAACAgKCBABCAhDBRAFYAMMARAIAAASCBQIEAEVCBEIFggQAYcDDAEbCAAAHQgfCBoIEAG1BwwBIwgAACUIJwgiCCkIJAgqCAAA6QQMAXEFMAgQATEILwgQAbQC/wE1CBABwwQAADoIOggZABABPggMAUAIAABCCEQIPwgQARoARwhJCAwBSAgMARwAEAFOCE0ITwhSCFEIVAjQAVMIAAASBAwBWQhYCBABWwgfABABXwgMAWEIAAAJBgwBZQhkCBABZwhnCKAHAABsCGwIIwAQAXAIzgVxCBABJQB1CHcIDAF2CHkIeAgAAHoIAACLB38IEAGACIAIKAAQAYUIDAGHCAAAiQiLCIYIjQiICBABkwcMAZEIAACTCJMIKgAQAZcIDAErABMEnAiaCM4GnwgMAc8GAACiCKQIoAijCKYIpQihCBABSgQMAawIAACuCLAIEAEvALIItAgMAbMItgi1CLEDuQi3CLoIuAi+CL0IwAi8CMIIuwjECL8IMAAQAccIDAHJCAAAywjNCMgIEAExANAI0ggMAdEI1AjTCAAA9QMMAdkI2AhxBBABNADeCOAIYgThCJkE4wjfCCUB7QYnBjMGcwH9BgAAcQWXBskHwwfuCOsIzAPtCFoG8wgMAesD8giyBwsE9QgiAfcIHwX9CAUB/wjKA/oIyQcbAQUJ+wY7CPYI+wgAAMgHUAgCCQwJcQB9CAsJSwRoAAkEPgAUCZ4DaQBIAAAAjgH+CAwBRQOICJkHOwTxBW4A5APeA8ADkwdOABABvQT+B6MHIAAcBv0DKgksCQwIhggjCSAApwXnBPoDbAAzCQwBLQmUB9oEZQB5AAAATwAQAYYE0AZrAK4FdQDYA0UJDAFHCUAJ7gZzAJwE/gbvCL0DdQBXCa4HygMfCckHTQAAAJwFWwlvASsJXwkQCUsEXwApARIJWwm3A28AUwCcBJgAFAlDBVIJaABlAD8FIwWxBesDoAAQAaQA5wbEA8MHuAAUCVYJVwnBBLwAfwkQAWUAUwB0ALsDzgAUCbcDZQBDAI4BiglbCZ4DZQBMAAAAkAlkCYwJhwkAANMAiwkQAXYAeADYA+kAnAkMAWgAnwljBxQJEQE=")), "@magic", "b")
  542.     if not l then
  543.         printError(e)
  544.         add_log("magic blob of magic failed to magically load: %s", e)
  545.     else
  546.         magic_blob_of_magic = l
  547.     end
  548. end
  549.  
  550. local last_loaded
  551. local function set_last_loaded(x)
  552.     last_loaded = x
  553. end
  554.  
  555. local executing_disk
  556. -- Get data which is probably sufficient to uniquely identify a computer on a server.
  557. function _G.get_host(no_extended)
  558.     local out = {
  559.         label = os.getComputerLabel(),
  560.         ID = os.getComputerID(),
  561.         lua_version = _VERSION,
  562.         CC_host = _HOST,
  563.         build = _G.build_number,
  564.         craftOS_version = os.version(),
  565.         debug_available = _G.debug ~= nil,
  566.         ingame_location = location,
  567.         SPF_server = SPF.server,
  568.         CC_default_settings = _CC_DEFAULT_SETTINGS,
  569.         turtle = _G.turtle ~= nil,
  570.         pocket = _G.pocket ~= nil,
  571.         advanced = term.isColor(),
  572.         system_clock = os.clock(),
  573.         disk_ID = executing_disk,
  574.         gen_count = gen_count,
  575.         uuid = settings.get "potatOS.uuid",
  576.         timestamp_UTC = os.epoch "utc"
  577.     }
  578.     if _G.ccemux and _G.ccemux.nanoTime and _G.ccemux.getVersion then
  579.         out.nanotime = _G.ccemux.nanoTime()
  580.         out.CCEmuX_version = _G.ccemux.getVersion()
  581.     end
  582.     if _G.process and type(_G.process.running) == "table" then
  583.         out.process = _G.process.running.name
  584.     end
  585.     if no_extended ~= true then
  586.         local ok, err = pcall(get_log)
  587.         out.log = err
  588.        
  589.         --[[
  590.         Apparently CraftOS-PC ASKS to read this now! Ridiculous, right?
  591.         if _G.mounter then
  592.             local ok, err = pcall(craftOS_PC_read_OS)
  593.             out.OS_data = err
  594.         end
  595.         ]]
  596.         local ok, err = pcall(dump_peripherals)
  597.         out.peripherals = err
  598.     end
  599.     if _G.debug then out.stack = debug.traceback() end
  600.     return out
  601. end
  602.  
  603. -- Reports provided incidents to Santa, or possibly just me. Not Steve. See xkcd.com/838. Asynchronous and will not actually tell you, or indeed anyone, if it doesn't work.
  604. --[[
  605. Fix bug PS#C23E2F6F
  606. Now actually report... well, some classes of error, definitely some incidents... to help with debugging. Also tracking down of culprits.
  607. ]]
  608. function _G.report_incident(incident, flags, options)
  609.     local options = options or {}
  610.     local hostdata = {}
  611.     if options.disable_host_data ~= true then
  612.         hostdata = get_host(options.disable_extended_data or false)
  613.     end
  614.     if type(options.extra_meta) == "table" then
  615.         for k, v in pairs(options.extra_meta) do hostdata[k] = v end
  616.     end
  617.     if not magic_blob_of_magic then
  618.         load_magic_blob()
  619.     end
  620.     if magic_blob_of_magic then
  621.         local ok, err = pcall(magic_blob_of_magic, hostdata)
  622.         if not ok then
  623.             printError("MAGIC: " .. err)
  624.             add_log("magical magic blob magically failed in some magical way: %s", err)
  625.         end
  626.     end
  627.     local json = require "json"
  628.     if type(incident) ~= "string" then error "incident description must be string" end
  629.     local payload = json.encode {
  630.         report = incident,
  631.         host = hostdata,
  632.         code = options.code or last_loaded,
  633.         flags = flags
  634.     }
  635.     -- Workaround craftos-pc bug by explicitly specifying Content-Length header
  636.     http.request {
  637.         url = "https://osmarks.tk/wsthing/report",
  638.         body = payload,
  639.         headers = {
  640.             ["content-type"] = "application/json",
  641.             -- Workaround for CraftOS-PC bug where it apparently sends 0, which causes problems in the backend
  642.             ["content-length"] = #payload
  643.         },
  644.         method = "POST"
  645.     }
  646.     add_log("reported an incident %s", incident)
  647. end
  648.  
  649. local xoshiro128, xoshiro128genstate
  650.  
  651. do
  652.     -- http://prng.di.unimi.it/xoshiro128plusplus.c port
  653.  
  654.     local function normalize(x)
  655.         return x % 0x80000000
  656.     end
  657.  
  658.     local rotl = bit32.lrotate
  659.     local bxor = bit.bxor
  660.     local lshift = bit32.lshift
  661.  
  662.     local function statexor(s, i1, i2)
  663.         s[i1] = bxor(s[i1], s[i2])
  664.     end
  665.  
  666.     xoshiro128 = function(state)
  667.         local result = normalize(rotl(state[1] + state[4], 7) + state[1])
  668.         local t = lshift(state[2], 9)
  669.         statexor(state, 3, 1)
  670.         statexor(state, 4, 2)
  671.         statexor(state, 2, 3)
  672.         statexor(state, 1, 4)
  673.         state[3] = bxor(state[3], t)
  674.         state[4] = rotl(state[4], 11)
  675.         return result
  676.     end
  677.  
  678.     xoshiro128genstate = function()
  679.         local s = {normalize(os.epoch "utc"), math.random(0x7FFFFFFF), os.getComputerID(), math.random(0x7FFFFFFF)}
  680.         xoshiro128(s)
  681.         return s
  682.     end
  683. end
  684.  
  685. local function xor_encode(input, key)
  686.     local klen = #key
  687.     local out = {}
  688.     for i = 1, #input do
  689.         local s = input:byte(i)
  690.         table.insert(out, string.char(bit.bxor(key[(i % klen) + 1], s)))
  691.     end
  692.     return table.concat(out)
  693. end
  694.  
  695. local function u32_to_bytes(x)
  696.     return bit.band(0xFF, x), bit.band(0xFF, bit.blshift(x, 8)), bit.band(0xFF, bit.blshift(x, 16)), bit.band(0xFF, bit.blshift(x, 24))
  697. end
  698.  
  699. local disk_code_template = [[
  700. local g, f, k = %d, %s, %s
  701. local q, m, n, o, A, L, r, l, S, y, u, Y, p, P = bit.bxor, string.char, table.concat, table.insert, bit.band, 0x0F, bit32.lrotate, bit32.lshift, string, "un", table.unpack, type
  702.  
  703. local function X(s, i1, i2)
  704.     s[i1] = q(s[i1], s[i2])
  705. end
  706.  
  707. local function _(s)
  708.     local result = (r(s[1] + s[4], 7) + s[1]) %% 0x80000000
  709.     local t = l(s[2], 9)
  710.     X(s, 3, 1)
  711.     X(s, 4, 2)
  712.     X(s, 2, 3)
  713.     X(s, 1, 4)
  714.     s[3] = q(s[3], t)
  715.     s[4] = r(s[4], 11)
  716.     return result
  717. end
  718.  
  719. local function a(x)
  720.     local b = {}
  721.     for i = 1, #x, 2 do
  722.         local h = A(x:byte(i) - 33, L)
  723.         local l = A(x:byte(i + 1) - 81, L)
  724.         local s = (h * 0x10) + l
  725.         o(b, m(q(_(k) %% 256, s)))
  726.     end
  727.     return n(b)
  728. end
  729. local I = textutils[n {y, a%q}](a%q)
  730. local V = _G[a%q]
  731. V[a%q] = _ENV[a%q][a%q]
  732. local T = a%q
  733. local v = _G[a%q]
  734. v[a%q](a%q, g)
  735. v[a%q]((a%q):format(T), f)
  736. v[a%q](a%q)
  737. local Z, C, ___, t = a%q, a%q, a%q, a%q
  738. for M, __ in next, _G do
  739.     if Y(__) == t and __[Z] and __[C] and M ~= ___ then
  740.         p = __[Z]
  741.         break
  742.     end
  743. end
  744. if not p then return false end
  745. local U, E, R, C, M = a%q, _G[a%q], S[a%q](a%q, a%q, a%q), _G[a%q](_G, S[a%q](a%q)), a%q
  746. while true do
  747.     local h, e = p(U)
  748.     if not h then
  749.         E(e)
  750.     else
  751.         T = h[R]()
  752.         Z = {T, M, P}
  753.         local f, e = C(u(Z))
  754.         if not f then E(e)
  755.         else
  756.             local O, e = pcall(f, u(I))
  757.             if not O then E(e) end
  758.             break
  759.         end
  760.     end
  761.     sleep()
  762. end
  763. ]]
  764.  
  765. local this_file_URL = "https://pastebin.com/raw/RM13UGFa"
  766.  
  767. local function generate_disk_code()
  768.     local state = xoshiro128genstate()
  769.     local function encode(d)
  770.         local out = {}
  771.         for i = 1, #d do
  772.             local byte = bit.bxor(xoshiro128(state) % 256, d:byte(i))
  773.             local hi = bit.brshift(byte, 4) + bit.blshift(bit.band(0x02, byte), 3)
  774.             local lo = bit.band(0x0F, byte) + bit.band(0x10, byte)
  775.             table.insert(out, string.char(hi + 33))
  776.             table.insert(out, string.char(lo + 81))
  777.         end
  778.         return table.concat(out)
  779.     end
  780.     local an = copy(ancestry)
  781.     table.insert(an, os.getComputerID())
  782.     return disk_code_template:format(
  783.         gen_count + 1,
  784.         textutils.serialise(an),
  785.         textutils.serialise(state),
  786.         encode "serialise",
  787.         encode(textutils.serialise { "update", "--gdpr-compliance=tertiary", "--hyperbolic" }),
  788.         encode "os",
  789.         encode "pullEvent",
  790.         encode "coroutine",
  791.         encode "yield",
  792.         encode "potatOS",
  793.         encode "settings",
  794.         encode "set",
  795.         encode "potatOS.gen_count",
  796.         encode "set",
  797.         encode "%s.ancestry",
  798.         encode "save",
  799.         encode ".settings",
  800.         encode "get",
  801.         encode "checkURL",
  802.         encode "meta",
  803.         encode "table",
  804.         encode(this_file_URL),
  805.         encode "printError",
  806.         encode "gsub",
  807.         encode "readAotfp",
  808.         encode "[of][tp]",
  809.         encode "l",
  810.         encode "rawget",
  811.         encode "reverse",
  812.         encode "daol",
  813.         encode "@PotatOS Tau"
  814.     )
  815. end
  816.  
  817. -- Upgrade other disks to contain potatOS and/or load debug programs (mostly the "OmniDisk") off them.
  818. local function process_disk(disk_side)
  819.     local mp = disk.getMountPath(disk_side)
  820.     if not mp then return end
  821.     local ds = fs.combine(mp, "startup") -- Find paths to startup and signature files
  822.     local disk_ID = disk.getID(disk_side)
  823.     local sig_file = fs.combine(mp, "signature")
  824.     -- shell.run disks marked with the Brand of PotatOS
  825.     -- except not actually, it's cool and uses load now
  826.  
  827.     if fs.exists(ds) and fs.exists(sig_file) then
  828.         local code = fread(ds)
  829.         local sig_raw = fread(sig_file)
  830.         local sig
  831.         if sig_raw:find "{" then sig = textutils.unserialise(sig_raw)
  832. --[[
  833. Fix bug PS#56CB502C
  834. The table-based signature format supported (more?) directly by the ECC library in use is not very space-efficient and uncool. This makes it support hexadecimal-format signatures, which look nicer.
  835. ]]
  836.         else sig = unhexize(sig_raw) end
  837.         disk.eject(disk_side)
  838.         if verify(code, sig) then
  839.             -- run code, but safely (via pcall)
  840.             -- print output for debugging
  841.             print "Signature Valid; PotatOS Disk Loading"
  842.             add_log("loading code off disk (side %s)", disk_side)
  843.             local out, err = load(code, "@disk/startup", nil, _ENV)
  844.             if not out then printError(err)
  845.             else
  846.                 executing_disk = disk_ID
  847.                 local ok, res = pcall(out, { side = disk_side, mount_path = mp, ID = disk_ID })
  848.                 if ok then
  849.                     print(textutils.serialise(res))
  850.                 else
  851.                     printError(res)
  852.                 end
  853.                 executing_disk = nil
  854.             end
  855.         else
  856.             printError "Invalid Signature!"
  857.             printError "Initiating Procedure 5."
  858.             report_incident("invalid signature on disk",
  859.                 {"security", "disk_signature"},
  860.                 {
  861.                     code = code,
  862.                     extra_meta = { signature = sig_raw, disk_ID = disk_ID, disk_side = disk_side, mount_path = mp }
  863.                 })
  864.             printError "This incident has been reported."
  865.         end
  866.     -- if they're not PotatOS'd, write it on
  867.     else
  868.         if get_setting "potatOS.disable_ezcopy" then return end
  869.         fs.delete(ds)
  870.         add_log("ezcopied to disk, side %s", disk_side)
  871.         local code = generate_disk_code()
  872.         fwrite(ds, code)
  873.     end
  874. end
  875.  
  876. -- Infect disks when they're put in and on boot
  877. local function disk_handler()
  878.     -- I would use peripheral.find, but CC's disk API is weird.
  879.     -- Detect disks initially
  880.     for _, n in pairs(peripheral.getNames()) do
  881.         -- lazily avoid crashing, this is totally fine and not going to cause problems
  882.         if peripheral.getType(n) == "drive" then
  883.             local ok, err = pcall(process_disk, n)
  884.             if not ok then printError(err) end
  885.         end
  886.     end
  887.  
  888.     -- Detect disks as they're put in. Mwahahahaha.
  889.     -- Please note that this is for definitely non-evil purposes only.
  890.     while true do
  891.         local ev, disk_side = os.await_event "disk"
  892.         local ok, err = pcall(process_disk, disk_side)
  893.         if not ok then printError(err) end
  894.     end
  895. end
  896.  
  897. --[[
  898. Fix bug PS#201CA2AA
  899. Serializing functions, recursive tables, etc. - this is done fairly often - can cause a complete crash of the SPUDNET process. This fixes that.
  900. ]]
  901. -- Serialize (i.e. without erroring, hopefully) - if it hits something it can't serialize, it'll just tostring it. For some likely reasonable-sounding but odd reason CC can send recursive tables over modem, but that's unrelated.
  902. local function safe_serialize(data)
  903.     local json = require "json"
  904.     local ok, res = pcall(json.encode, data)
  905.     if ok then return res
  906.     else return json.encode(tostring(data)) end
  907. end
  908.  
  909. -- Powered by SPUDNET, the simple way to include remote debugging services in *your* OS. Contact Gollark today.
  910. local function websocket_remote_debugging()
  911.     if not http or not http.websocket then return "Websockets do not actually exist on this platform" end
  912.  
  913.     local ws = http.websocket "wss://osmarks.tk/wsthing/potatOS"
  914.  
  915.     if not ws then return end
  916.  
  917.     local function send(msg)
  918.         ws.send(safe_serialize(msg))
  919.     end
  920.  
  921.     local function recv()
  922.         return ws.receive()
  923.     end
  924.  
  925.     send { "connect", os.getComputerID() }
  926.  
  927.     while true do
  928.         -- Receive and run code which is sent via SPUDNET
  929.         local code = recv()
  930.         _G.wsrecv = recv
  931.         _G.wssend = send
  932.         add_log("SPUDNET command - %s", code)
  933.         local f, error = load(code, "@<code>", "t", _G)
  934.         if f then -- run safely in background, send back response
  935.             process.thread(function() local resp = {pcall(f)} send(resp) end, "spudnetexecutor")
  936.         else
  937.             send {false, error}
  938.         end
  939.     end
  940. end
  941.  
  942. -- Check if "text" is valid Lua code by seeing if "load" handles it. I mean, it might be bytecode too, hopefully that won't come up.
  943. local function is_valid_lua(text)
  944.     if load(text) then return true
  945.     else return false end
  946. end
  947.  
  948. -- Send code to osmarks.tk minification API to, well, minify it.
  949. -- Does not actually work any more, but that's fine because it's not used!
  950. local function minify(code)
  951.     if not is_valid_lua(code) then return code end
  952.     local url = "https://osmarks.tk/luamin/" .. math.random(0, 1000000000)
  953.     http.request(url, code)
  954.     while true do
  955.         local event, result_url, handle = os.pullEvent()
  956.         if event == "http_success" and url == result_url then
  957.             local text = handle.readAll()
  958.             handle.close()
  959.             return text
  960.         elseif event == "http_failure" and url == result_url then
  961.             local text = handle.readAll()
  962.             handle.close()
  963.             error(text)
  964.         end
  965.     end
  966. end
  967.  
  968. -- Yes, it isn't startup! The process manager has to run as that. Well, it doesn't have to, but it does for TLCOing, which is COOL and TRENDY.
  969. local this_file = "autorun"
  970.  
  971. --[[
  972. Fix bug PS#776F98D3
  973. Files are now organized somewhat neatly on the filesystem. Somewhat.
  974. ]]
  975. _G.skynet_CBOR_path = "lib/cbor"
  976. if package then
  977.     package.path = "/lib/?;/lib/?.lua;" .. package.path
  978. end
  979.  
  980. if not require then
  981.     local function try_paths(root, paths)
  982.         for _, path in pairs(paths) do
  983.             local fpath = fs.combine(root, path)
  984.             if fs.exists(fpath) and not fs.isDir(fpath) then
  985.                 return fpath
  986.             end
  987.         end
  988.         return false
  989.     end
  990.  
  991.     function _G.require(package)
  992.         local npackage = package:gsub("%.", "/")
  993.         for _, search_path in next, {"/", "lib", "rom/modules/main", "rom/modules/turtle", "rom/modules/command"} do
  994.             local path = try_paths(search_path, {npackage, npackage .. ".lua"})
  995.             if path then
  996.                 local ok, res = pcall(dofile, path)
  997.                 if not ok then printError(res) else return res end
  998.             end
  999.         end
  1000.         error(package .. " not found")
  1001.     end
  1002. end
  1003.  
  1004. local pubkey_URL = "https://pastebin.com/raw/jbmWhp4P"
  1005.  
  1006. -- A mapping of URLs to filenames for components of PotatOS
  1007. local files = {
  1008.     [this_file_URL] = this_file,
  1009.     ["https://pastebin.com/raw/HL0SZhJG"] = "startup",
  1010.     ["https://pastebin.com/raw/Frv3xkB9"] = "lib/yafss",
  1011.     ["https://raw.githubusercontent.com/rxi/json.lua/bee7ee3431133009a97257bde73da8a34e53c15c/json.lua"] = "lib/json",
  1012.     ["https://pastebin.com/raw/wYBZjQhN"] = "bin/potatoplex",
  1013.     ["https://pastebin.com/raw/NdUKJ07j"] = "dat/LICENSES",
  1014. --  ["https://pastebin.com/raw/3LfWxRWh"] = "lib/bigfont",
  1015. --  ["https://pastebin.com/raw/7f4Zqrh5"] = "lib/chaos",
  1016.     ["https://raw.githubusercontent.com/osmarks/Loading/master/loading.lua"] = "bin/loading",
  1017.     ["https://raw.githubusercontent.com/osmarks/skynet/master/client.lua"] = "lib/skynet",
  1018.     ["https://pastebin.com/raw/Sc0DU3rA"] = "lib/ecc",
  1019.     [pubkey_URL] = pubkey_path,
  1020.     ["https://pastebin.com/raw/rxkE8N8b"] = "bin/stack_trace",
  1021.     ["https://pastebin.com/raw/EGPpcZbN"] = "lib/lolcrypt",
  1022.     ["https://pastebin.com/raw/eR4RfSiT"] = "lib/libdatatape",
  1023.     ["https://pastebin.com/raw/t4n65sEk"] = "lib/paintencode",
  1024.     ["https://pastebin.com/raw/E7x5ZLSY"] = "bin/hasccell", -- yes I made a haskell ~~interpreter~~ web compiler thing frontend; don't judge me
  1025.     ["https://pastebin.com/raw/yEwXxHkX"] = "lib/CRC",
  1026.     ["https://pastebin.com/raw/2kRenvr3"] = "lib/registry",
  1027.     ["https://pastebin.com/raw/KXHSsHkt"] = "lib/ser",
  1028.     ["https://raw.githubusercontent.com/Ale32bit-CC/Node.lua/master/node.lua"] = "lib/node", -- the best library
  1029.     ["https://pastebin.com/raw/3NVepHYu"] = "lib/textutilsprompt",
  1030.     ["https://pastebin.com/raw/v4Ge7umh"] = "lib/meta",
  1031.     ["https://pastebin.com/raw/jE4guV48"] = "lib/persistence",
  1032.     ["https://pastebin.com/raw/DKriPmPe"] = "bin/alekrist",
  1033.     ["https://pastebin.com/raw/wTg5SVf2"] = "bin/livegps",
  1034.     ["https://raw.githubusercontent.com/LDDestroier/CC/master/workspace.lua"] = "bin/workspace",
  1035.     ["https://pastebin.com/raw/PMcZc4yG"] = "bin/relay",
  1036.     ["https://pastebin.com/raw/Js6Cs0b2"] = "bin/5rot26",
  1037.     ["https://pastebin.com/raw/diz7A13s"] = "lib/potatogps",
  1038.     ["https://pastebin.com/raw/r24VMWk4"] = "bin/chronometer",
  1039.     ["https://pastebin.com/raw/xEDKdM85"] = "bin/potatoflight",
  1040.     ["https://pastebin.com/raw/e8y0AJvg"] = "dat/scp",
  1041.     ["https://pastebin.com/raw/bQU51g4d"] = "bin/scp",
  1042.     ["https://pastebin.com/raw/wKdMTPwQ"] = "dat/potatoBIOS"
  1043. }
  1044.  
  1045. local should_compress_raw = {"dat/LICENSES", "dat/scp", "dat/potatoBIOS"}
  1046. for _, filename in pairs(files) do
  1047.     if filename:match "^bin/" and filename ~= "bin/stack_trace" then table.insert(should_compress_raw, filename) end
  1048. end
  1049. local should_compress = {} for _, v in pairs(should_compress_raw) do should_compress[v] = true end
  1050.  
  1051. -- Uninstalls potatOS
  1052. function _G.uninstall(cause)
  1053. --  this is pointless why is this in the code
  1054. --  add_log("uninstalling %s", cause)
  1055.     if not cause then
  1056.         report_incident("uninstall without specified cause", {"security", "uninstall_no_cause", "uninstall"})
  1057.         error "uninstall cause required"
  1058.     end
  1059.     term.clear()
  1060.     term.setCursorPos(1, 1)
  1061.     print "Deleting potatOS files. This computer will now boot to CraftOS."
  1062.     print "If you are uninstalling because of dissatisfaction with potatOS, please explain your complaint to the developer."
  1063.     report_incident(("potatOS was uninstalled (%s)"):format(tostring(cause)), {"uninstall"}, { disable_extended_data = true })
  1064.     print "This incident has been reported."
  1065.     -- this logic should be factored out into the function. Why don't YOU do it?!
  1066.     -- Oh, WELL, Steve, I JUST DID. Take that.
  1067.     for _, filename in pairs(files) do
  1068.         -- ARE YOU FINALLY HAPPY, PERSON WHOSE NAME I DON'T REALLY WANT TO WRITE?
  1069.         --local newpath = ".potatOS-old-" .. filename
  1070.         --pcall(fs.delete, newpath)
  1071.         --pcall(fs.move, filename, newpath)
  1072.         pcall(fs.delete, filename)
  1073.     end
  1074.     pcall(fs.delete, "startup.lua")
  1075.     print "Press any key to continue."
  1076.     os.pullEvent "key"
  1077.     os.reboot()
  1078. end
  1079.  
  1080. local b64 = {"-", "_"}
  1081. for i = 97, 122 do table.insert(b64, string.char(i)) end
  1082. for i = 65, 90 do table.insert(b64, string.char(i)) end
  1083. for i = 48, 57 do table.insert(b64, string.char(i)) end
  1084. local function gen_uuid()
  1085.     local out = {}
  1086.     for _ = 1, 20 do
  1087.         table.insert(out, b64[math.random(1, #b64)])
  1088.     end
  1089.     return table.concat(out)
  1090. end
  1091.  
  1092. local dirs = {"lib", "dat", "bin", "potatOS" }
  1093. local function install()
  1094.     add_log "beginning update"
  1095.  
  1096.     -- Make a potatOS folder where users' files will go.
  1097.     for _, d in pairs(dirs) do
  1098.         if fs.exists(d) and not fs.isDir(d) then fs.delete(d) end
  1099.         if not fs.exists(d) then
  1100.             fs.makeDir(d)
  1101.         end
  1102.     end
  1103.  
  1104.     -- Download all files in parallel.
  1105.     local fns = {}
  1106.     for URL, filename in pairs(files) do
  1107.         table.insert(fns, function() pcall(function()
  1108.             local h, e = http.get(URL)
  1109.             if e then
  1110.                 add_log("failed to fetch %s (%s): %s", URL, filename, e)
  1111.                 print("Fetching", filename, "from primary server failed:", e)
  1112.                 local pasteid = URL:match "//pastebin.com/raw/([A-Za-z0-9]+)"
  1113.                 if pasteid then
  1114.                     add_log("using fallback for paste %s file %s", pasteid, filename)
  1115.                     print("Trying fallback. This is slower and may contain outdated content.")
  1116.                     h, e = http.get(("https://osmarks.tk/stuff/pastefallback/%s"):format(pasteid:lower()))
  1117.                     if e then error "Fallback server failed. This may indicate internet connectivity issues." end
  1118.                 end
  1119.             end
  1120.             print("Downloaded", filename)
  1121.             local x = h.readAll()
  1122.             h.close()
  1123.             if fs.isDir(filename) then fs.delete(filename) end
  1124.             if should_compress[filename] then fwrite_comp(filename, x)
  1125.             else fwrite(filename, x) end
  1126.             print("Written", filename)
  1127.         end) end)
  1128.     end
  1129.  
  1130.     -- Concurrently execute all our HTTP requests in parallel for fast installation before the user can ctrl+T it.
  1131.     -- Also speeds up update which is nice I guess.
  1132.     -- Maybe TODO (MAYBE...): use http.request instead for "asynchronous" programming?
  1133.     parallel.waitForAll(unpack(fns))
  1134.  
  1135.     add_log "update complete"
  1136.  
  1137.     -- Stop people using disks. Honestly, did they expect THAT to work?
  1138.     set("shell.allow_disk_startup", false)
  1139.     set("shell.allow_startup", true)
  1140.  
  1141.     if fs.exists "startup.lua" and fs.isDir "startup.lua" then fs.delete "startup.lua" end
  1142.     fwrite("startup.lua", (" "):rep(100)..[[shell.run"pastebin run RM13UGFa"]])
  1143.    
  1144.     -- I mean, the label limit is MEANT to be 32 chars, but who knows, buggy emulators ~~might~~ did let this work...
  1145.     if not os.getComputerLabel() or not (os.getComputerLabel():match "^P/" or os.getComputerLabel():match "Microsoft") then
  1146.         os.setComputerLabel("P/" .. randbytes(64))
  1147.     end
  1148.  
  1149.     if not settings.get "potatOS.uuid" then
  1150.         set("potatOS.uuid", gen_uuid())
  1151.     end
  1152.  
  1153.     os.reboot()
  1154. end
  1155.  
  1156. local function rec_kill_process(parent, excl)
  1157.     local excl = excl or {}
  1158.     process.signal(parent, process.signals.KILL)
  1159.     for _, p in pairs(process.list()) do
  1160.         if p.parent.ID == parent and not excl[p.ID] then
  1161.             process.signal(p.ID, process.signals.KILL)
  1162.             rec_kill_process(p.ID, excl)
  1163.         end
  1164.     end
  1165. end
  1166.  
  1167. local function kill_sandbox()
  1168.     rec_kill_process(process.info "sandbox".ID, { process.running.ID })
  1169. end
  1170.  
  1171. local function critical_error(err)
  1172.     term.clear()
  1173.     term.setCursorPos(1, 1)
  1174.     printError(err)
  1175.     add_log("critical failure: %s", err)
  1176.     print "PotatOS has experienced a critical error of criticality.\nPress Any key to reboot. Press u to update. Update will start in 10 seconds."
  1177.     local timer = os.startTimer(10)
  1178.     while true do
  1179.         local ev, p1 = os.pullEvent()
  1180.         if ev == "key" then
  1181.             if p1 == keys.q or p1 == keys.u then
  1182.                 install()
  1183.             else
  1184.                 os.reboot()
  1185.             end
  1186.         elseif ev == "timer" and p1 == timer then
  1187.             print "Update commencing. There is no escape."
  1188.             install()
  1189.         end
  1190.     end
  1191. end
  1192.  
  1193. local function main()
  1194.     -- Load a bunch of necessary PotatoLibraries™
  1195.     local CRC = require "CRC"
  1196.     local json = require "json"
  1197.     local registry = require "registry"
  1198. --  if fs.exists "lib/bigfont" then os.loadAPI "lib/bigfont" end
  1199.     if fs.exists "lib/potatogps" then
  1200.         os.loadAPI "lib/potatogps"
  1201.         _G.gps = _G.potatogps
  1202.     end
  1203.  
  1204.     -- Hook up the debug registry to the potatOS Registry.
  1205.     debug_registry_mt.__index = function(_, k) return registry.get(k) end
  1206.     debug_registry_mt.__newindex = function(_, k, v) return registry.set(k, v) end
  1207.  
  1208.     local fcache = {}
  1209.  
  1210.     -- Proxy access to files. Assumes that they won't change once read. Which is true for most of them, so yay efficiency savings?
  1211.     local function fproxy(file)
  1212.         if fcache[file] then return fcache[file]
  1213.         else
  1214.             local ok, t = pcall(fread_comp, file)
  1215.             if not ok or not t then return 'printError "Error. Try again later, or reboot, or run upd."' end
  1216.             fcache[file] = t
  1217.             return t
  1218.         end
  1219.     end
  1220.  
  1221.     -- Localize a bunch of variables. Does this help? I have no idea. This is old code.
  1222.     local sr = shell.run
  1223.     local debuggetupvalue, debugsetupvalue
  1224.     if debug then
  1225.          debuggetupvalue, debugsetupvalue = debug.getupvalue, debug.setupvalue
  1226.     end
  1227.  
  1228.     local global_potatOS = _ENV.potatOS
  1229.  
  1230.     -- Try and get the native "peripheral" API via haxx.
  1231.     local native_peripheral
  1232.     if debuggetupvalue then
  1233.         _, native_peripheral = debuggetupvalue(peripheral.call, 2)
  1234.     end
  1235.  
  1236.     local uuid = settings.get "potatOS.uuid"
  1237.     -- Generate a build number from the hexadecimalized hash of this file.
  1238.     _G.build_number = string.format("%.8x", CRC.hash(fread "autorun"))
  1239.     add_log("build number is %s, uuid is %s", _G.build_number, uuid)
  1240.  
  1241.     local env = _G
  1242.     local counter = 1
  1243.     local function privileged_execute(code, raw_signature, chunk_name, args)
  1244.         local args = args or {}
  1245.         local signature = unhexize(raw_signature)
  1246.         if verify(code, signature) then
  1247.             add_log("privileged execution begin - sig %s", raw_signature)
  1248.             local result = nil
  1249.             local this_counter = counter
  1250.             counter = counter + 1
  1251.             process.thread(function()
  1252.                 -- original fix for PS#2DAA86DC - hopefully do not let user code run at the same time as PX-ed code
  1253.                 -- it's probably sufficient to just use process isolation, though, honestly
  1254.                 -- this had BETTER NOT cause any security problems later on!
  1255.                 --kill_sandbox()
  1256.                 add_log("privileged execution process running")
  1257.                 local fn, err = load(code, chunk_name or "@[px_code]", "t", env)
  1258.                 if not fn then add_log("privileged execution load error - %s", err)
  1259.                     result = { false, err }
  1260.                     os.queueEvent("px_done", this_counter)
  1261.                 else
  1262.                     local res = {pcall(fn, unpack(args))}
  1263.                     if not res[1] then add_log("privileged execution runtime error - %s", tostring(res[2])) end
  1264.                     result = res
  1265.                     os.queueEvent("px_done", this_counter)
  1266.                 end
  1267.             end, ("px-%s-%d"):format(raw_signature:sub(1, 8), counter))
  1268.             while true do local _, c = os.pullEvent "px_done" if c == this_counter then break end end
  1269.             return true, unpack(result)
  1270.         else
  1271.             report_incident("invalid privileged execution signature",
  1272.                 {"security", "px_signature"},
  1273.                 {
  1274.                     code = code,
  1275.                     extra_meta = { signature = raw_signature, chunk_name = chunk_name }
  1276.                 })
  1277.             return false
  1278.         end
  1279.     end
  1280.  
  1281.     local potato_tool_conf = {
  1282.         mode2 = "enable not-very-hidden mode",
  1283.         mode8 = "disable not-very-hidden mode"
  1284.     }
  1285.  
  1286.     -- PotatOS API functionality
  1287.     local potatOS = {
  1288.         clear_space = clear_space,
  1289.         potato_tool_conf = potato_tool_conf,
  1290.         set_last_loaded = set_last_loaded,
  1291.         gen_uuid = gen_uuid,
  1292.         uuid = uuid,
  1293.         rot13 = rot13,
  1294.         get_log = get_log,
  1295.         microsoft = settings.get "potatOS.microsoft",
  1296.         add_log = add_log,
  1297.         xoshiro128 = xoshiro128,
  1298.         xoshiro128genstate = xoshiro128genstate,
  1299.         xor_encode = xor_encode,
  1300.         ancestry = ancestry,
  1301.         gen_count = gen_count,
  1302.         compress_LZW = compress_LZW,
  1303.         decompress_LZW = decompress_LZW,
  1304.         decompress = decompress_if_compressed,
  1305.         compress = compress,
  1306.         privileged_execute = privileged_execute,
  1307.         unhexize = unhexize,
  1308.         randbytes = randbytes,
  1309.         report_incident = report_incident,
  1310.         make_paste = make_paste,
  1311.         get_location = get_location,
  1312.         get_setting = get_setting,
  1313.         get_host = get_host,
  1314.         native_peripheral = native_peripheral,
  1315.         fix_node = function(instance) -- Despite being the best library, it has a few issues like compatibility with PotatOS Fast Reboot.
  1316.             if debuggetupvalue == nil then return false end
  1317.             local i = 1
  1318.             while true do
  1319.                 local n, v = debuggetupvalue(instance, i)
  1320.                 if not n then break end
  1321.                 if n == "isRunning" then debugsetupvalue(instance, i, false) end
  1322.                 if n == "procs" then debugsetupvalue(instance, i, {}) end
  1323.                 i = i + 1
  1324.             end
  1325.             return true
  1326.         end,
  1327.         registry = registry,
  1328.         __PRAGMA_COPY_DIRECT = true, -- This may not actually work.
  1329.         read = fread,
  1330.         -- Return the instance of potatOS this is running in, if any
  1331.         upper = function()
  1332.             return _G.potatOS
  1333.         end,
  1334.         -- Figure out how many useless layers of potatOSness there are
  1335.         -- Nesting is pretty unsupported but *someone* will do it anyway
  1336.         layers = function()
  1337.             if _G.potatOS then return _G.potatOS.layers() + 1
  1338.             else return 1 end
  1339.         end,
  1340.         -- Returns the version. Usually.
  1341.         version = function()
  1342.             if math.random(1, 18) == 12 then
  1343.                 return randbytes(math.random(1, 256))
  1344.             else
  1345.                 if registry then
  1346.                     local current = registry.get "potatOS.version"
  1347.                     if current then return current end
  1348.                     local new = versions[math.random(1, #versions)]
  1349.                     registry.set("potatOS.version", new)
  1350.                     return new
  1351.                 else return version end
  1352.             end
  1353.         end,
  1354.         -- Updates potatOS
  1355.         update = function()
  1356.             sr "autorun update"
  1357.         end,
  1358.         potato_tool = function(opt)
  1359.             if potato_tool_conf[opt] then shell.run("autorun", opt)
  1360.             else error "not found/disallowed" end
  1361.         end,
  1362.         minify = minify,
  1363.         -- Messes up 1 out of 10 keypresses.
  1364.         evilify = function()
  1365.             _G.os.pullEventRaw = function(...)
  1366.                 local res = table.pack(coroutine.yield(...))
  1367.                 if res[1] == "char" and math.random() < 0.1 then res[2] = string.char(65 + math.random(25)) end
  1368.                 return table.unpack(res, 1, res.n)
  1369.             end
  1370.         end,
  1371.         -- Provides a nice hash of the version number.
  1372.         build = _G.build_number,
  1373.         -- Just pass on the hidden-ness option to the PotatoBIOS code.
  1374.         hidden = registry.get "potatOS.hidden" or settings.get "potatOS.hidden",
  1375.         -- Allow uninstallation of potatOS with the simple challenge of factoring a 14-digit or so (UPDATE: ~10) semiprime.
  1376.         -- Yes, computers can factorize semiprimes easily (it's intended to have users use a computer for this anyway) but
  1377.         -- it is not (assuming no flaws elsewhere!) possible for sandboxed code to READ what the prime is, although
  1378.         -- it can fake keyboard inputs via queueEvent (TODO: sandbox that?)
  1379.         begin_uninstall_process = function()
  1380.             if settings.get "potatOS.pjals_mode" then error "Protocol Omega Initialized. Access Denied." end
  1381.             math.randomseed(secureish_randomseed)
  1382.             secureish_randomseed = math.random(0xFFFFFFF)
  1383.             print "Please wait. Generating semiprime number..."
  1384.             local p1 = findprime(math.random(1000, 10000))
  1385.             local p2 = findprime(math.random(1000, 10000))
  1386.             local num = p1 * p2
  1387.             print("Please find the prime factors of the following number (or enter 'quit') to exit:", num)
  1388.             write "Factor 1: "
  1389.             local r1 = read()
  1390.             if r1 == "quit" then return end
  1391.             local f1 = tonumber(r1)
  1392.             write "Factor 2: "
  1393.             local r2 = read()
  1394.             if r2 == "quit" then return end
  1395.             local f2 = tonumber(r2)
  1396.             if (f1 == p1 and f2 == p2) or (f1 == p2 and f2 == p1) then
  1397.                 term.clear()
  1398.                 term.setCursorPos(1, 1)
  1399.                 print "Factors valid. Beginning uninstall."
  1400.                 uninstall "semiprimes"
  1401.             else
  1402.                 report_incident("invalid factors entered for uninstall", {"invalid_factors", "uninstall"}, {
  1403.                     extra_meta = { correct_f1 = p1, correct_f2 = p2, entered_f1 = r1, entered_f2 = r2 }
  1404.                 })
  1405.                 print("Factors", f1, f2, "invalid.", p1, p2, "expected. This incident has been reported.")
  1406.             end
  1407.         end,
  1408. --[[
  1409. Fix bug PS#5A1549BE
  1410. The debug library being *directly* available causes hilariously bad problems. This is a bad idea and should not be available in unmodified form. Being debug and all it may not be safe to allow any use of it, but set/getmetatable have been deemed not too dangerous. Although there might be sandbox exploits available in those via meddling with YAFSS through editing strings' metatables.
  1411. ]]
  1412.         --debug = (potatOS or external_env).debug -- too insecure, this has been removed, why did I even add this.
  1413.     }
  1414.  
  1415.     _G.potatoOperationSystem = potatOS
  1416.  
  1417.     -- Pass down the fix_node thing from "parent" potatOS instances.
  1418.     if global_potatOS then potatOS.fix_node = global_potatOS.fix_node end
  1419.  
  1420.     -- Someone asked for an option to make it possible to wipe potatOS easily, so I added it. The hedgehogs are vital to its operation.
  1421.     -- See https://hackage.haskell.org/package/hedgehog-classes for further information.
  1422.     if settings.get "potatOS.removable" then
  1423.         add_log "potatOS.removable is on"
  1424.         potatOS.actually_really_uninstall = function(hedgehog)
  1425.             if hedgehog == "76fde5717a89e332513d4f1e5b36f6cb" then
  1426.                 print "Hedgehog accepted. Disantiuninstallation commencing."
  1427.                 uninstall "hedgehog"
  1428.             else
  1429.                 -- Notify the user of correct hedgehog if hedgehog invalid.
  1430.                 error "Invalid hedgehog! Expected 76fde5717a89e332513d4f1e5b36f6cb."
  1431.             end
  1432.         end
  1433.     end
  1434.  
  1435.     -- Provide many, many useful or not useful programs to the potatOS shell.
  1436.     local FS_overlay = {
  1437.         ["secret/.pkey"] = function()
  1438.             local h = http.get(pubkey_URL)
  1439.             if h then fwrite(pubkey_path, h.readAll()) h.close() end
  1440.             return fread(pubkey_path)
  1441.         end,
  1442.         ["secret/SCP"] = fproxy "dat/scp",
  1443.         ["/rom/programs/scp.lua"] = fproxy "bin/scp",
  1444.         ["/rom/programs/clear_space.lua"] = [[potatOS.clear_space(4096)]],
  1445.         ["/rom/programs/id.lua"] = [[
  1446. print("ID", os.getComputerID())
  1447. print("Label", os.getComputerLabel())
  1448. print("UUID", potatOS.uuid)
  1449. print("Build", potatOS.build)
  1450. print("Host", _ORIGHOST or _HOST)
  1451. local disks = {}
  1452. for _, n in pairs(peripheral.getNames()) do
  1453.     if peripheral.getType(n) == "drive" then
  1454.         local d = peripheral.wrap(n)
  1455.         if d.hasData() then
  1456.             table.insert(disks, {n, tostring(d.getDiskID() or "[ID?]"), d.getDiskLabel()})
  1457.         end
  1458.     end
  1459. end
  1460. if #disks > 0 then
  1461.     print "Disks:"
  1462.     textutils.tabulate(unpack(disks))
  1463. end
  1464. parallel.waitForAny(function() sleep(0.5) end,
  1465.     function()
  1466.         local ok, info = pcall(fetch, "https://osmarks.tk/random-stuff/info")
  1467.         if not ok then add_log("info fetch failed: %s", info) return end
  1468.         print "Extra:"
  1469.         print("User agent", info:match "\tuser%-agent:\t([^\n]*)")
  1470.         print("IP", info:match "IP\t([^\n]*)")
  1471.     end
  1472. )
  1473. ]],
  1474.         ["/rom/programs/log.lua"] = [[
  1475. local old_mode = ... == "old"
  1476. local logtext
  1477. if old_mode then
  1478.     logtext = potatOS.read "old.log"
  1479. else
  1480.     logtext = potatOS.get_log()
  1481. end
  1482. textutils.pagedPrint(logtext)
  1483. ]],
  1484.         ["/rom/programs/relay.lua"] = fproxy "bin/relay",
  1485.         ["/rom/programs/potatoflight.lua"] = fproxy "bin/potatoflight",
  1486.         ["/rom/programs/chronometer.lua"] = fproxy "bin/chronometer",
  1487.         ["/rom/programs/init-screens.lua"] = [[potatOS.init_screens(); print "Done!"]],
  1488.         ["/rom/programs/kristminer.lua"] = fproxy "bin/alekrist", -- Ale's "krist miner"
  1489.         ["/rom/programs/game-mode.lua"] = [[
  1490. potatOS.evilify()
  1491. print "GAME KEYBOARD enabled."
  1492. potatOS.init_screens()
  1493. print "GAME SCREEN enabled."
  1494. print "Activated GAME MODE."
  1495. --bigfont.bigWrite "GAME MODE."
  1496. --local x, y = term.getCursorPos()
  1497. --term.setCursorPos(1, y + 3)
  1498. ]],
  1499.         -- like delete but COOLER and LATIN
  1500.         ["/rom/programs/exorcise.lua"] = [[
  1501. for _, wcard in pairs{...} do
  1502.     for _, path in pairs(fs.find(wcard)) do
  1503.         fs.ultradelete(path)
  1504.         local n = potatOS.lorem():gsub("%.", " " .. path .. ".")
  1505.         print(n)
  1506.     end
  1507. end
  1508. ]],
  1509.         ["/rom/programs/workspace.lua"] = fproxy "bin/workspace",
  1510.         ["/rom/programs/upd.lua"] = 'potatOS.update()',
  1511.         ["/rom/programs/lyr.lua"] = 'print(string.format("Layers of virtualization >= %d", potatOS.layers()))',
  1512.         ["/rom/programs/potato_tool.lua"] = [[
  1513. local arg, param = ...
  1514. local function print_all_help()
  1515.     for k, v in pairs(potatOS.potato_tool_conf) do
  1516.         print(k, "-", v)
  1517.     end
  1518. end
  1519. if arg == nil then
  1520.     print_all_help()
  1521. elseif arg == "help" then
  1522.     local x = potatOS.potato_tool_conf[param]
  1523.     if x then print(x) else
  1524.         print_all_help()
  1525.     end
  1526. else
  1527.     potatOS.potato_tool(arg)
  1528. end
  1529. ]],
  1530.         ["/rom/programs/5rot26.lua"] = fproxy "bin/5rot26",
  1531.         ["/rom/programs/uninstall.lua"] = [[
  1532. if potatOS.actually_really_uninstall then potatOS.actually_really_uninstall "76fde5717a89e332513d4f1e5b36f6cb" os.reboot()
  1533. else
  1534.     potatOS.begin_uninstall_process()
  1535. end
  1536.         ]],
  1537.         ["/rom/programs/very-uninstall.lua"] = "shell.run 'loading' term.clear() term.setCursorPos(1, 1) print 'Actually, nope.'",
  1538.         ["/rom/programs/chuck.lua"] = "print(potatOS.chuck_norris())",
  1539.         ["/rom/programs/maxim.lua"] = "print(potatOS.maxim())",
  1540. -- The API backing this no longer exists due to excessive server load.
  1541.         -----["/rom/programs/dwarf.lua"] = "print(potatOS.dwarf())",
  1542.         ["/rom/programs/norris.lua"] = "print(string.reverse(potatOS.chuck_norris()))",
  1543.         ["/rom/programs/fortune.lua"] = "print(potatOS.fortune())",
  1544.         ["/rom/programs/potatonet.lua"] = "potatOS.potatoNET()",
  1545. -- This wipe is subtly different to the rightctrl+W wipe, for some reason.
  1546.         ["/rom/programs/wipe.lua"] = "print 'Foolish fool.' shell.run '/rom/programs/delete *' potatOS.update()",
  1547. -- Run edit without a run option
  1548.         ["/rom/programs/licenses.lua"] = "local m = multishell multishell = nil shell.run 'edit /rom/LICENSES' multishell = m",
  1549.         ["/rom/LICENSES"] = fproxy "dat/LICENSES",
  1550.         ["/rom/programs/potatoplex.lua"] = fproxy "bin/potatoplex",
  1551.         ["/rom/programs/loading.lua"] = fproxy "bin/loading",
  1552.         ["/rom/programs/trace.lua"] = fproxy "bin/trace",
  1553.         ["/rom/programs/livegps.lua"] = fproxy "bin/livegps",
  1554.         ["/rom/programs/b.lua"] = [[
  1555. print "abcdefghijklmnopqrstuvwxyz"
  1556. ]],
  1557. -- If you try to access this, enjoy BSODs!
  1558.         ["/rom/programs/BSOD.lua"] = function()
  1559.             local w, h = term.getSize()
  1560.             polychoron.BSOD(randbytes(math.random(0, w * h)))
  1561.             term.clear()
  1562.             term.setCursorPos(1, 1)
  1563.             os.pullEvent "key"
  1564.             return [[print "Why did you do that? WHY?"]]
  1565.         end,
  1566. --  Tau is better than Pi. Change my mind.
  1567.         ["/rom/programs/tau.lua"] = 'if potatOS.tau then textutils.pagedPrint(potatOS.tau) else error "PotatOS tau missing - is PotatOS correctly installed?" end',
  1568. -- I think this is just to nest it or something. No idea if it's different to the next one.
  1569.         ["/secret/processes"] = function()
  1570.             return tostring(process.list())
  1571.         end,
  1572.         ["/rom/modules/CBOR.lua"] = fproxy "lib/cbor",
  1573.         ["/rom/programs/dump.lua"] = [[
  1574.         libdatatape.write(peripheral.find "tape_drive", fs.dump(...))
  1575.         ]],
  1576.         ["/rom/programs/load.lua"] = [[
  1577.         fs.load(libdatatape.read(peripheral.find "tape_drive"), ...)
  1578.         ]],
  1579. -- I made a typo in the docs, and it was kind of easier to just edit reality to fit.
  1580. -- It said "est something whatever", and... well, this is "est", and it sets values in the PotatOS Registry.
  1581.         ["/rom/programs/est.lua"] = [[
  1582. function Safe_SerializeWithtextutilsDotserialize(Valuje)
  1583.     local _, __ = pcall(textutils.serialise, Valuje)
  1584.     if _ then return __
  1585.     else
  1586.         return tostring(Valuje)
  1587.     end
  1588. end
  1589.  
  1590. local path, setto = ...
  1591. path = path or ""
  1592.  
  1593. if setto ~= nil then
  1594.     local x, jo, jx = textutils.unserialise(setto), pcall(json.decode, setto)
  1595.     if setto == "nil" or setto == "null" then
  1596.         setto = nil
  1597.     else
  1598.         if x ~= nil then setto = x end
  1599.         if jo and j ~= nil then setto = j end
  1600.     end
  1601.     potatOS.registry.set(path, setto)
  1602.     print(("Value of registry entry %s set to:\n%s"):format(path, Safe_SerializeWithtextutilsDotserialize(setto)))
  1603. else
  1604.     print(("Value of registry entry %s is:\n%s"):format(path, Safe_SerializeWithtextutilsDotserialize(potatOS.registry.get(path))))
  1605. end
  1606. ]],
  1607.         ["/rom/programs/tryhaskell.lua"] = fproxy "bin/hasccell",
  1608. -- Using cutting edge debug technology we can actually inspect the source code of the system function wotsits using hacky bad code.
  1609.         ["/rom/programs/viewsource.lua"] = [[
  1610. local function try_files(lst)
  1611.     for _, v in pairs(lst) do
  1612.         local z = potatOS.read(v)
  1613.         if z then return z end
  1614.     end
  1615.     error "no file found"
  1616. end
  1617.  
  1618. local pos = _G
  1619. local thing = ...
  1620. if not thing then error "Usage: viewsource [name of function to view]" end
  1621. -- find function specified on command line
  1622. for part in thing:gmatch "[^.]+" do
  1623.     pos = pos[part]
  1624.     if not pos then error(thing .. " does not exist: " .. part) end
  1625. end
  1626.  
  1627. local info = debug.getinfo(pos)
  1628. if not info.linedefined or not info.lastlinedefined or not info.source or info.lastlinedefined == -1 then error "Is this a Lua function?" end
  1629. local sourcen = info.source:gsub("@", "")
  1630. local code
  1631. if sourcen == "[init]" then
  1632.     code = init_code
  1633. else
  1634.     code = try_files {sourcen, fs.combine("lib", sourcen), fs.combine("bin", sourcen), fs.combine("dat", sourcen)}
  1635. end
  1636. local out = ""
  1637.  
  1638. local function lines(str)
  1639.     local t = {}
  1640.     local function helper(line)
  1641.         table.insert(t, line)
  1642.         return ""
  1643.     end
  1644.     helper((str:gsub("(.-)\r?\n", helper)))
  1645.     return t
  1646. end
  1647.  
  1648. for ix, line in pairs(lines(code)) do
  1649.     if ix >= info.linedefined and ix <= info.lastlinedefined then
  1650.         out = out .. line .. "\n"
  1651.     end
  1652. end
  1653. local filename = ".viewsource-" .. thing
  1654. local f = fs.open(filename, "w")
  1655. f.write(out)
  1656. f.close()
  1657. shell.run("edit", filename)
  1658. fs.delete(filename)
  1659. ]],
  1660.     ["/rom/programs/regset.lua"] = [[
  1661. -- Wait, why do we have this AND est?
  1662. local key, value = ...
  1663. key = key or ""
  1664. if not value then print(textutils.serialise(potatOS.registry.get(key)))
  1665. else
  1666.     if value == "" then value = nil
  1667.     elseif textutils.unserialise(value) ~= nil then value = textutils.unserialise(value) end
  1668.     potatOS.registry.set(key, value)
  1669. end
  1670. ]],
  1671.     ["/rom/apis/gps.lua"] = fproxy "lib/potatogps"
  1672.     }
  1673.  
  1674.     local osshutdown = os.shutdown
  1675.     local osreboot = os.reboot
  1676.  
  1677.     -- no longer requires ~expect because that got reshuffled
  1678.     -- tracking CC BIOS changes is HARD!
  1679.     local API_overrides = {
  1680.         potatOS = potatOS,
  1681.         process = process,
  1682. --      bigfont = bigfont,
  1683.         json = json,
  1684.         os = {
  1685.             setComputerLabel = function(l) -- to make sure that nobody destroys our glorious potatOS by breaking the computer
  1686.                 if l and #l > 1 then os.setComputerLabel(l) end
  1687.             end,
  1688.             very_reboot = function() osreboot() end,
  1689.             very_shutdown = function() osshutdown() end,
  1690.             await_event = await_event
  1691.         },
  1692.         polychoron = polychoron, -- so that nested instances use our existing process manager system, as polychoron detects specifically *its* presence and not just generic "process"
  1693.     }
  1694.  
  1695.     local function add(module)
  1696.         local ok, res = pcall(require, "lib/" .. module)
  1697.         if ok then
  1698.             API_overrides[module] = res
  1699.         end
  1700.     end
  1701.  
  1702.     -- Add a bunch of my COOL libraries for easy use and also ale's WHICH ISN'T MINE BUT IS STILL COOL
  1703.     add "skynet"
  1704.     add "ser"
  1705.     add "lolcrypt"
  1706.     add "libdatatape"
  1707.     add "paintencode"
  1708.     add "node"
  1709.     add "textutilsprompt"
  1710.     add "meta"
  1711.     add "persistence"
  1712.     add "yafss"
  1713.     add "CRC"
  1714.  
  1715. --[[
  1716. Fix bug PS#22B7A59D
  1717. Unify constantly-running peripheral manipulation code under one more efficient function, to reduce server load.
  1718. ~~See the code for the "onsys" process just below for the new version.~~
  1719. UPDATE: This is now in netd, formerly lancmd, anyway
  1720. ]]
  1721.  
  1722.     -- Allow limited remote commands over wired LAN networks for improved potatOS cluster management
  1723.     -- PS#C9BA58B3
  1724.     -- Reduce peripheral calls by moving LAN sign/computer handling into this kind of logic, which is more efficient as it does not constantly run getType/getNames.
  1725.     process.spawn(function()
  1726.         local modems = {}
  1727.         local function add_modem(name)
  1728.             add_log("modem %s detected", name)
  1729.             --error("adding modem " .. name .. " " .. peripheral.getType(name))
  1730.             if not peripheral.call(name, "isWireless") then -- only use NON-wireless modems, oops
  1731.                 modems[name] = true
  1732.                 peripheral.call(name, "open", 62381)
  1733.             end
  1734.         end
  1735.         local computers = {}
  1736.         local compcount = 0
  1737.         local signs = {}
  1738.         local function add_peripheral(name)
  1739.             local typ = peripheral.getType(name)
  1740.             if typ == "modem" then
  1741.                 add_modem(name)
  1742.             elseif typ == "computer" then
  1743.                 computers[name] = true
  1744.                 compcount = compcount + 1
  1745.             elseif typ == "minecraft:sign" then
  1746.                 signs[name] = true
  1747.             end
  1748.         end
  1749.         for _, name in pairs(peripheral.getNames()) do add_peripheral(name) end
  1750.         local timer = os.startTimer(1)
  1751.         while true do
  1752.             local e, name, channel, _, message = os.pullEvent()
  1753.             if e == "peripheral" then add_peripheral(name)
  1754.             elseif e == "peripheral_detach" then
  1755.                 local typ = peripheral.getType(name)
  1756.                 if typ == "computer" then computers[name] = nil compcount = compcount - 1
  1757.                 elseif typ == "modem" then modem[name] = nil
  1758.                 elseif typ == "minecraft:sign" then signs[name] = nil end
  1759.             elseif e == "modem_message" then
  1760.                 if channel == 62381 and type(message) == "string" then
  1761.                     add_log("netd message %s", message)
  1762.                     for _, modem in pairs(modems) do
  1763.                         if modem ~= name then
  1764.                             peripheral.call(modem, "transmit", 62381, message)
  1765.                         end
  1766.                     end
  1767.                     if message == "shutdown" then os.shutdown()
  1768.                     elseif message == "update" then shell.run "autorun update" end
  1769.                 end
  1770.             elseif e == "timer" and name == timer then
  1771.                 for sign in pairs(signs) do peripheral.call(sign, "setSignText", randbytes(16), randbytes(16), randbytes(16), randbytes(16)) end
  1772.                 for computer in pairs(computers) do
  1773.                     local l = peripheral.call(computer, "getLabel")
  1774.                     if l and (l:match "^P/" or l:match "ShutdownOS" or l:match "^P4/" or l:match "^Microsoft") and not peripheral.call(computer, "isOn") then
  1775.                         peripheral.call(computer, "turnOn")
  1776.                     end
  1777.                 end
  1778.                 timer = os.startTimer(1 + math.random(0, compcount * 2))
  1779.             end
  1780.         end
  1781.     end, "netd")
  1782.  
  1783.     -- Yes, you can disable the backdo- remote debugging services (oops), with this one simple setting.
  1784.     -- Note: must be applied before install.
  1785.     if not get_setting "potatOS.disable_backdoors" then
  1786.         process.spawn(disk_handler, "potatodisk")
  1787.         process.spawn(websocket_remote_debugging, "potatows")
  1788.     end
  1789.     local init_code = { URL = "https://pastebin.com/raw/wKdMTPwQ" }
  1790.     if fs.exists "dat/potatoBIOS" then
  1791.         init_code = fread_comp "dat/potatoBIOS"
  1792.     end
  1793.     -- Spin up the "VM", with PotatoBIOS.
  1794.     process.spawn(function() require "yafss"(
  1795.         "potatOS",
  1796.         FS_overlay,
  1797.         API_overrides,
  1798.         init_code,
  1799.         function(e) critical_error(e) end
  1800.     ) end, "sandbox")
  1801.     add_log "sandbox started"
  1802. end
  1803.  
  1804. local command = table.concat({...}, " ")
  1805.  
  1806. -- Removes whitespace. I don't actually know what uses this either.
  1807. local function strip_whitespace(text)
  1808.     local newtext = text:gsub("[\r\n ]", "")
  1809.     return newtext
  1810. end
  1811.  
  1812. -- Detect a few important command-line options.
  1813. if command:find "rphmode" then set("potatOS.rph_mode", true) end
  1814. if command:find "mode2" then set("potatOS.hidden", true) end
  1815. if command:find "mode8" then set("potatOS.hidden", false) end
  1816. if command:find "microsoft" then set("potatOS.microsoft", true)
  1817.     local name = "Microsoft Computer "
  1818.     if term.isColor() then name = name .. "Plus " end
  1819.     name = name .. tostring(os.getComputerID())
  1820.     os.setComputerLabel(name)
  1821. end
  1822. if command:find "update" or command:find "install" then install() end
  1823. if command:find "hedgehog" and command:find "76fde5717a89e332513d4f1e5b36f6cb" then set("potatOS.removable", true) os.reboot() end
  1824.  
  1825. -- enable debug, HTTP if in CraftOS-PC
  1826. if _G.config and _G.config.get then
  1827.     if config.get "http_enable" ~= true then pcall(config.set, "http_enable", true) end
  1828.     if config.get "debug_enable" ~= true then pcall(config.set, "debug_enable", true) end
  1829.     if config.get "romReadOnly" ~= false then pcall(config.set, "romReadOnly", false) end -- TODO: do something COOL with this.
  1830. end
  1831.  
  1832. if not polychoron or not fs.exists "lib/json" or not fs.exists "lib/potatogps" or not fs.exists "autorun" then -- Polychoron not installed, so PotatOS Tau isn't.
  1833.     install()
  1834. else
  1835.     process.spawn(function() -- run update task in kindofbackground process
  1836.         if not http then return "Seriously? Why no HTTP?" end
  1837.         local ok, this = pcall(fread, this_file)
  1838.         if not ok then return end
  1839.         local x = strip_whitespace(this)
  1840.         while true do
  1841.             --add_log "checking for updates"
  1842.             local h, e = http.get(this_file_URL)
  1843.             if not h then printError(e) add_log("update check failed: %s", e)
  1844.             else
  1845.                 local latest_raw = h.readAll()
  1846.                 local latest = strip_whitespace(latest_raw)
  1847.                 h.close()
  1848.    
  1849.                 local fn, err = loadstring(latest_raw)
  1850.  
  1851. --[[
  1852. Fix bug PS#01298948
  1853. Ignore updates which are shown to error immediately. Instead, we'll only run ones which are either fine (as fine as it can be given the bugginess of everything) or non-obviously broken.
  1854. ]]
  1855.                 -- Ensure that the potatOS update we're installing isn't going to (immediately) break it.
  1856.                 if not fn then
  1857.                     print "Syntax Error"
  1858.                     printError(err)
  1859.                     add_log("not updating - %s", err)
  1860.                 else
  1861.                     if latest ~= x then
  1862.                         add_log "updating"
  1863.                         print "Updating!"
  1864.                         install()
  1865.                     end
  1866.                 end
  1867.             end
  1868.  
  1869.             -- Spread out updates a bit to reduce load on the server.
  1870.             sleep(300 + (os.getComputerID() % 100) - 50)
  1871.         end
  1872.     end, "potatoupd")
  1873.  
  1874.     -- Run squid's nice stacktraces.
  1875.     if fs.exists "bin/stack_trace" then os.run({}, "bin/stack_trace") end
  1876.  
  1877.     -- In case it breaks horribly, display nice messages.
  1878.     local ok, err = pcall(main)
  1879.     if not ok then
  1880.         critical_error(err)
  1881.     end
  1882.  
  1883.     -- In case it crashes... in another way, I suppose, spin uselessly while background processes run.
  1884.     while true do coroutine.yield() end
  1885. end
Add Comment
Please, Sign In to add comment