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