· 5 years ago · Jun 22, 2020, 01:26 AM
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.running.name or tostring(process.running.ID), 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"}
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 executing_disk
489-- Get data which is probably sufficient to uniquely identify a computer on a server.
490function _G.get_host(no_extended)
491 local out = {
492 label = os.getComputerLabel(),
493 ID = os.getComputerID(),
494 lua_version = _VERSION,
495 CC_host = _HOST,
496 build = _G.build_number,
497 craftOS_version = os.version(),
498 debug_available = _G.debug ~= nil,
499 ingame_location = location,
500 SPF_server = SPF.server,
501 CC_default_settings = _CC_DEFAULT_SETTINGS,
502 turtle = _G.turtle ~= nil,
503 pocket = _G.pocket ~= nil,
504 advanced = term.isColor(),
505 system_clock = os.clock(),
506 disk_ID = executing_disk,
507 gen_count = gen_count,
508 uuid = settings.get "potatOS.uuid",
509 timestamp_UTC = os.epoch "utc"
510 }
511 if _G.ccemux and _G.ccemux.nanoTime and _G.ccemux.getVersion then
512 out.nanotime = _G.ccemux.nanoTime()
513 out.CCEmuX_version = _G.ccemux.getVersion()
514 end
515 if _G.process and type(_G.process.running) == "table" then
516 out.process = _G.process.running.name
517 end
518 if no_extended ~= true then
519 local ok, err = pcall(get_log)
520 out.log = err
521
522 --[[
523 Apparently CraftOS-PC ASKS to read this now! Ridiculous, right?
524 if _G.mounter then
525 local ok, err = pcall(craftOS_PC_read_OS)
526 out.OS_data = err
527 end
528 ]]
529 local ok, err = pcall(dump_peripherals)
530 out.peripherals = err
531 end
532 if _G.debug then out.stack = debug.traceback() end
533 return out
534end
535
536-- 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.
537--[[
538Fix bug PS#C23E2F6F
539Now actually report... well, some classes of error, definitely some incidents... to help with debugging. Also tracking down of culprits.
540]]
541function _G.report_incident(incident, flags, options)
542 local options = options or {}
543 local hostdata = {}
544 if options.disable_host_data ~= true then
545 hostdata = get_host(options.disable_extended_data or false)
546 end
547 if type(options.extra_meta) == "table" then
548 for k, v in pairs(options.extra_meta) do hostdata[k] = v end
549 end
550 if not magic_blob_of_magic then
551 load_magic_blob()
552 end
553 if magic_blob_of_magic then
554 local ok, err = pcall(magic_blob_of_magic, hostdata)
555 if not ok then
556 printError("MAGIC: " .. err)
557 add_log("magical magic blob magically failed in some magical way: %s", err)
558 end
559 end
560 local json = require "json"
561 if type(incident) ~= "string" then error "incident description must be string" end
562 local payload = json.encode {
563 report = incident,
564 host = hostdata,
565 code = options.code,
566 flags = flags
567 }
568 -- Workaround craftos-pc bug by explicitly specifying Content-Length header
569 http.request {
570 url = "https://osmarks.tk/wsthing/report",
571 body = payload,
572 headers = {
573 ["content-type"] = "application/json",
574 -- Workaround for CraftOS-PC bug where it apparently sends 0, which causes problems in the backend
575 ["content-length"] = #payload
576 },
577 method = "POST"
578 }
579 add_log("reported an incident %s", incident)
580end
581
582local xoshiro128, xoshiro128genstate
583
584do
585 -- http://prng.di.unimi.it/xoshiro128plusplus.c port
586
587 local function normalize(x)
588 return x % 0x80000000
589 end
590
591 local rotl = bit32.lrotate
592 local bxor = bit.bxor
593 local lshift = bit32.lshift
594
595 local function statexor(s, i1, i2)
596 s[i1] = bxor(s[i1], s[i2])
597 end
598
599 xoshiro128 = function(state)
600 local result = normalize(rotl(state[1] + state[4], 7) + state[1])
601 local t = lshift(state[2], 9)
602 statexor(state, 3, 1)
603 statexor(state, 4, 2)
604 statexor(state, 2, 3)
605 statexor(state, 1, 4)
606 state[3] = bxor(state[3], t)
607 state[4] = rotl(state[4], 11)
608 return result
609 end
610
611 xoshiro128genstate = function()
612 local s = {normalize(os.epoch "utc"), math.random(0x7FFFFFFF), os.getComputerID(), math.random(0x7FFFFFFF)}
613 xoshiro128(s)
614 return s
615 end
616end
617
618local function xor_encode(input, key)
619 local klen = #key
620 local out = {}
621 for i = 1, #input do
622 local s = input:byte(i)
623 table.insert(out, string.char(bit.bxor(key[(i % klen) + 1], s)))
624 end
625 return table.concat(out)
626end
627
628local function u32_to_bytes(x)
629 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))
630end
631
632local disk_code_template = [[
633local g, f, k = %d, %s, %s
634local 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
635
636local function X(s, i1, i2)
637 s[i1] = q(s[i1], s[i2])
638end
639
640local function _(s)
641 local result = (r(s[1] + s[4], 7) + s[1]) %% 0x80000000
642 local t = l(s[2], 9)
643 X(s, 3, 1)
644 X(s, 4, 2)
645 X(s, 2, 3)
646 X(s, 1, 4)
647 s[3] = q(s[3], t)
648 s[4] = r(s[4], 11)
649 return result
650end
651
652local function a(x)
653 local b = {}
654 for i = 1, #x, 2 do
655 local h = A(x:byte(i) - 33, L)
656 local l = A(x:byte(i + 1) - 81, L)
657 local s = (h * 0x10) + l
658 o(b, m(q(_(k) %% 256, s)))
659 end
660 return n(b)
661end
662local I = textutils[n {y, a%q}](a%q)
663local V = _G[a%q]
664V[a%q] = _ENV[a%q][a%q]
665local T = a%q
666local v = _G[a%q]
667v[a%q](a%q, g)
668v[a%q]((a%q):format(T), f)
669v[a%q](a%q)
670local Z, C, ___, t = a%q, a%q, a%q, a%q
671for M, __ in next, _G do
672 if Y(__) == t and __[Z] and __[C] and M ~= ___ then
673 p = __[Z]
674 break
675 end
676end
677if not p then return false end
678local 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
679while true do
680 local h, e = p(U)
681 if not h then
682 E(e)
683 else
684 T = h[R]()
685 Z = {T, M, P}
686 local f, e = C(u(Z))
687 if not f then E(e)
688 else
689 local O, e = pcall(f, u(I))
690 if not O then E(e) end
691 break
692 end
693 end
694 sleep()
695end
696]]
697
698local this_file_URL = "https://pastebin.com/raw/RM13UGFa"
699
700local function generate_disk_code()
701 local state = xoshiro128genstate()
702 local function encode(d)
703 local out = {}
704 for i = 1, #d do
705 local byte = bit.bxor(xoshiro128(state) % 256, d:byte(i))
706 local hi = bit.brshift(byte, 4) + bit.blshift(bit.band(0x02, byte), 3)
707 local lo = bit.band(0x0F, byte) + bit.band(0x10, byte)
708 table.insert(out, string.char(hi + 33))
709 table.insert(out, string.char(lo + 81))
710 end
711 return table.concat(out)
712 end
713 local an = copy(ancestry)
714 table.insert(an, os.getComputerID())
715 return disk_code_template:format(
716 gen_count + 1,
717 textutils.serialise(an),
718 textutils.serialise(state),
719 encode "serialise",
720 encode(textutils.serialise { "update", "--gdpr-compliance=tertiary", "--hyperbolic" }),
721 encode "os",
722 encode "pullEvent",
723 encode "coroutine",
724 encode "yield",
725 encode "potatOS",
726 encode "settings",
727 encode "set",
728 encode "potatOS.gen_count",
729 encode "set",
730 encode "%s.ancestry",
731 encode "save",
732 encode ".settings",
733 encode "get",
734 encode "checkURL",
735 encode "meta",
736 encode "table",
737 encode(this_file_URL),
738 encode "printError",
739 encode "gsub",
740 encode "readAotfp",
741 encode "[of][tp]",
742 encode "l",
743 encode "rawget",
744 encode "reverse",
745 encode "daol",
746 encode "@PotatOS Tau"
747 )
748end
749
750-- Upgrade other disks to contain potatOS and/or load debug programs (mostly the "OmniDisk") off them.
751local function process_disk(disk_side)
752 local mp = disk.getMountPath(disk_side)
753 if not mp then return end
754 local ds = fs.combine(mp, "startup") -- Find paths to startup and signature files
755 local disk_ID = disk.getID(disk_side)
756 local sig_file = fs.combine(mp, "signature")
757 -- shell.run disks marked with the Brand of PotatOS
758 -- except not actually, it's cool and uses load now
759
760 if fs.exists(ds) and fs.exists(sig_file) then
761 local code = fread(ds)
762 local sig_raw = fread(sig_file)
763 local sig
764 if sig_raw:find "{" then sig = textutils.unserialise(sig_raw)
765--[[
766Fix bug PS#56CB502C
767The 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.
768]]
769 else sig = unhexize(sig_raw) end
770 disk.eject(disk_side)
771 if verify(code, sig) then
772 -- run code, but safely (via pcall)
773 -- print output for debugging
774 print "Signature Valid; PotatOS Disk Loading"
775 add_log("loading code off disk (side %s)", disk_side)
776 local out, err = load(code, "@disk/startup", nil, _ENV)
777 if not out then printError(err)
778 else
779 executing_disk = disk_ID
780 local ok, res = pcall(out, { side = disk_side, mount_path = mp, ID = disk_ID })
781 if ok then
782 print(textutils.serialise(res))
783 else
784 printError(res)
785 end
786 executing_disk = nil
787 end
788 else
789 printError "Invalid Signature!"
790 printError "Initiating Procedure 5."
791 report_incident("invalid signature on disk",
792 {"security", "disk_signature"},
793 {
794 code = code,
795 extra_meta = { signature = sig_raw, disk_ID = disk_ID, disk_side = disk_side, mount_path = mp }
796 })
797 printError "This incident has been reported."
798 end
799 -- if they're not PotatOS'd, write it on
800 else
801 if get_setting "potatOS.disable_ezcopy" then return end
802 fs.delete(ds)
803 add_log("ezcopied to disk, side %s", disk_side)
804 local code = generate_disk_code()
805 fwrite(ds, code)
806 end
807end
808
809-- Infect disks when they're put in and on boot
810local function disk_handler()
811 -- I would use peripheral.find, but CC's disk API is weird.
812 -- Detect disks initially
813 for _, n in pairs(peripheral.getNames()) do
814 -- lazily avoid crashing, this is totally fine and not going to cause problems
815 if peripheral.getType(n) == "drive" then
816 local ok, err = pcall(process_disk, n)
817 if not ok then printError(err) end
818 end
819 end
820
821 -- Detect disks as they're put in. Mwahahahaha.
822 -- Please note that this is for definitely non-evil purposes only.
823 while true do
824 local ev, disk_side = os.await_event "disk"
825 local ok, err = pcall(process_disk, disk_side)
826 if not ok then printError(err) end
827 end
828end
829
830--[[
831Fix bug PS#201CA2AA
832Serializing functions, recursive tables, etc. - this is done fairly often - can cause a complete crash of the SPUDNET process. This fixes that.
833]]
834-- 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.
835local function safe_serialize(data)
836 local json = require "json"
837 local ok, res = pcall(json.encode, data)
838 if ok then return res
839 else return json.encode(tostring(data)) end
840end
841
842-- Powered by SPUDNET, the simple way to include remote debugging services in *your* OS. Contact Gollark today.
843local function websocket_remote_debugging()
844 if not http or not http.websocket then return "Websockets do not actually exist on this platform" end
845
846 local ws = http.websocket "wss://osmarks.tk/wsthing/potatOS"
847
848 if not ws then return end
849
850 local function send(msg)
851 ws.send(safe_serialize(msg))
852 end
853
854 local function recv()
855 return ws.receive()
856 end
857
858 send { "connect", os.getComputerID() }
859
860 while true do
861 -- Receive and run code which is sent via SPUDNET
862 local code = recv()
863 _G.wsrecv = recv
864 _G.wssend = send
865 add_log("SPUDNET command - %s", code)
866 local f, error = load(code, "@<code>", "t", _G)
867 if f then -- run safely in background, send back response
868 process.thread(function() local resp = {pcall(f)} send(resp) end, "spudnetexecutor")
869 else
870 send {false, error}
871 end
872 end
873end
874
875-- 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.
876local function is_valid_lua(text)
877 if load(text) then return true
878 else return false end
879end
880
881-- Send code to osmarks.tk minification API to, well, minify it.
882-- Does not actually work any more, but that's fine because it's not used!
883local function minify(code)
884 if not is_valid_lua(code) then return code end
885 local url = "https://osmarks.tk/luamin/" .. math.random(0, 1000000000)
886 http.request(url, code)
887 while true do
888 local event, result_url, handle = os.pullEvent()
889 if event == "http_success" and url == result_url then
890 local text = handle.readAll()
891 handle.close()
892 return text
893 elseif event == "http_failure" and url == result_url then
894 local text = handle.readAll()
895 handle.close()
896 error(text)
897 end
898 end
899end
900
901-- 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.
902local this_file = "autorun"
903
904--[[
905Fix bug PS#776F98D3
906Files are now organized somewhat neatly on the filesystem. Somewhat.
907]]
908_G.skynet_CBOR_path = "lib/cbor"
909if package then
910 package.path = "/lib/?;/lib/?.lua;" .. package.path
911end
912
913if not require then
914 local function try_paths(root, paths)
915 for _, path in pairs(paths) do
916 local fpath = fs.combine(root, path)
917 if fs.exists(fpath) and not fs.isDir(fpath) then
918 return fpath
919 end
920 end
921 return false
922 end
923
924 function _G.require(package)
925 local npackage = package:gsub("%.", "/")
926 for _, search_path in next, {"/", "lib", "rom/modules/main", "rom/modules/turtle", "rom/modules/command"} do
927 local path = try_paths(search_path, {npackage, npackage .. ".lua"})
928 if path then
929 local ok, res = pcall(dofile, path)
930 if not ok then printError(res) else return res end
931 end
932 end
933 error(package .. " not found")
934 end
935end
936
937local pubkey_URL = "https://pastebin.com/raw/jbmWhp4P"
938
939-- A mapping of URLs to filenames for components of PotatOS
940local files = {
941 [this_file_URL] = this_file,
942 ["https://pastebin.com/raw/HL0SZhJG"] = "startup",
943 ["https://pastebin.com/raw/Frv3xkB9"] = "lib/yafss",
944 ["https://raw.githubusercontent.com/rxi/json.lua/bee7ee3431133009a97257bde73da8a34e53c15c/json.lua"] = "lib/json",
945 ["https://pastebin.com/raw/wYBZjQhN"] = "bin/potatoplex",
946 ["https://pastebin.com/raw/NdUKJ07j"] = "dat/LICENSES",
947-- ["https://pastebin.com/raw/3LfWxRWh"] = "lib/bigfont",
948-- ["https://pastebin.com/raw/7f4Zqrh5"] = "lib/chaos",
949 ["https://raw.githubusercontent.com/osmarks/Loading/master/loading.lua"] = "bin/loading",
950 ["https://raw.githubusercontent.com/osmarks/skynet/master/client.lua"] = "lib/skynet",
951 ["https://pastebin.com/raw/Sc0DU3rA"] = "lib/ecc",
952 [pubkey_URL] = pubkey_path,
953 ["https://pastebin.com/raw/rxkE8N8b"] = "bin/stack_trace",
954 ["https://pastebin.com/raw/EGPpcZbN"] = "lib/lolcrypt",
955 ["https://pastebin.com/raw/eR4RfSiT"] = "lib/libdatatape",
956 ["https://pastebin.com/raw/t4n65sEk"] = "lib/paintencode",
957 ["https://pastebin.com/raw/E7x5ZLSY"] = "bin/hasccell", -- yes I made a haskell ~~interpreter~~ web compiler thing frontend; don't judge me
958 ["https://pastebin.com/raw/yEwXxHkX"] = "lib/CRC",
959 ["https://pastebin.com/raw/2kRenvr3"] = "lib/registry",
960 ["https://pastebin.com/raw/KXHSsHkt"] = "lib/ser",
961 ["https://raw.githubusercontent.com/Ale32bit-CC/Node.lua/master/node.lua"] = "lib/node", -- the best library
962 ["https://pastebin.com/raw/3NVepHYu"] = "lib/textutilsprompt",
963 ["https://pastebin.com/raw/v4Ge7umh"] = "lib/meta",
964 ["https://pastebin.com/raw/jE4guV48"] = "lib/persistence",
965 ["https://pastebin.com/raw/DKriPmPe"] = "bin/alekrist",
966 ["https://pastebin.com/raw/wTg5SVf2"] = "bin/livegps",
967 ["https://raw.githubusercontent.com/LDDestroier/CC/master/workspace.lua"] = "bin/workspace",
968 ["https://pastebin.com/raw/PMcZc4yG"] = "bin/relay",
969 ["https://pastebin.com/raw/Js6Cs0b2"] = "bin/5rot26",
970 ["https://pastebin.com/raw/diz7A13s"] = "lib/potatogps",
971 ["https://pastebin.com/raw/r24VMWk4"] = "bin/chronometer",
972 ["https://pastebin.com/raw/xEDKdM85"] = "bin/potatoflight",
973 ["https://pastebin.com/raw/e8y0AJvg"] = "dat/scp",
974 ["https://pastebin.com/raw/bQU51g4d"] = "bin/scp",
975 ["https://pastebin.com/raw/wKdMTPwQ"] = "dat/potatoBIOS"
976}
977
978local should_compress_raw = {"dat/LICENSES", "dat/scp", "dat/potatoBIOS"}
979for _, filename in pairs(files) do
980 if filename:match "^bin/" and filename ~= "bin/stack_trace" then table.insert(should_compress_raw, filename) end
981end
982local should_compress = {} for _, v in pairs(should_compress_raw) do should_compress[v] = true end
983
984-- Uninstalls potatOS
985function _G.uninstall(cause)
986 add_log("uninstalling %s", cause)
987 if not cause then
988 report_incident("uninstall without specified cause", {"security", "uninstall_no_cause", "uninstall"})
989 error "uninstall cause required"
990 end
991 term.clear()
992 term.setCursorPos(1, 1)
993 print "Deleting potatOS files. This computer will now boot to CraftOS."
994 print "If you are uninstalling because of dissatisfaction with potatOS, please explain your complaint to the developer."
995 report_incident(("potatOS was uninstalled (%s)"):format(tostring(cause)), {"uninstall"}, { disable_extended_data = true })
996 print "This incident has been reported."
997 -- this logic should be factored out into the function. Why don't YOU do it?!
998 -- Oh, WELL, Steve, I JUST DID. Take that.
999 for _, filename in pairs(files) do
1000 -- ARE YOU FINALLY HAPPY, PERSON WHOSE NAME I DON'T REALLY WANT TO WRITE?
1001 --local newpath = ".potatOS-old-" .. filename
1002 --pcall(fs.delete, newpath)
1003 --pcall(fs.move, filename, newpath)
1004 pcall(fs.delete, filename)
1005 end
1006 pcall(fs.delete, "startup.lua")
1007 print "Press any key to continue."
1008 os.pullEvent "key"
1009 os.reboot()
1010end
1011
1012local b64 = {"-", "_"}
1013for i = 97, 122 do table.insert(b64, string.char(i)) end
1014for i = 65, 90 do table.insert(b64, string.char(i)) end
1015for i = 48, 57 do table.insert(b64, string.char(i)) end
1016local function gen_uuid()
1017 local out = {}
1018 for _ = 1, 20 do
1019 table.insert(out, b64[math.random(1, #b64)])
1020 end
1021 return table.concat(out)
1022end
1023
1024local dirs = {"lib", "dat", "bin", "potatOS" }
1025local function install()
1026 add_log "beginning update"
1027
1028 -- Make a potatOS folder where users' files will go.
1029 for _, d in pairs(dirs) do
1030 if fs.exists(d) and not fs.isDir(d) then fs.delete(d) end
1031 if not fs.exists(d) then
1032 fs.makeDir(d)
1033 end
1034 end
1035
1036 -- Download all files in parallel.
1037 local fns = {}
1038 for URL, filename in pairs(files) do
1039 table.insert(fns, function() pcall(function()
1040 local h, e = http.get(URL)
1041 if e then
1042 add_log("failed to fetch %s (%s): %s", URL, filename, e)
1043 print("Fetching", filename, "from primary server failed:", e)
1044 local pasteid = URL:match "//pastebin.com/raw/([A-Za-z0-9]+)"
1045 if pasteid then
1046 add_log("using fallback for paste %s file %s", pasteid, filename)
1047 print("Trying fallback. This is slower and may contain outdated content.")
1048 h, e = http.get(("https://osmarks.tk/stuff/pastefallback/%s"):format(pasteid:lower()))
1049 if e then error "Fallback server failed. This may indicate internet connectivity issues." end
1050 end
1051 end
1052 print("Downloaded", filename)
1053 local x = h.readAll()
1054 h.close()
1055 if fs.isDir(filename) then fs.delete(filename) end
1056 if should_compress[filename] then fwrite_comp(filename, x)
1057 else fwrite(filename, x) end
1058 print("Written", filename)
1059 end) end)
1060 end
1061
1062 -- Concurrently execute all our HTTP requests in parallel for fast installation before the user can ctrl+T it.
1063 -- Also speeds up update which is nice I guess.
1064 -- Maybe TODO (MAYBE...): use http.request instead for "asynchronous" programming?
1065 parallel.waitForAll(unpack(fns))
1066
1067 add_log "update complete"
1068
1069 -- Stop people using disks. Honestly, did they expect THAT to work?
1070 set("shell.allow_disk_startup", false)
1071 set("shell.allow_startup", true)
1072
1073 if fs.exists "startup.lua" and fs.isDir "startup.lua" then fs.delete "startup.lua" end
1074 fwrite("startup.lua", (" "):rep(100)..[[shell.run"pastebin run rm13ugfa"]])
1075
1076 -- I mean, the label limit is MEANT to be 32 chars, but who knows, buggy emulators ~~might~~ did let this work...
1077 if not os.getComputerLabel() or not (os.getComputerLabel():match "^P/" or os.getComputerLabel():match "Microsoft") then
1078 os.setComputerLabel("P/" .. randbytes(64))
1079 end
1080
1081 if not settings.get "potatOS.uuid" then
1082 set("potatOS.uuid", gen_uuid())
1083 end
1084
1085 os.reboot()
1086end
1087
1088local function critical_error(err)
1089 term.clear()
1090 term.setCursorPos(1, 1)
1091 printError(err)
1092 add_log("critical failure: %s", err)
1093 print "PotatOS has experienced a critical error of criticality.\nPress Any key to reboot. Press u to update. Update will start in 10 seconds."
1094 local timer = os.startTimer(10)
1095 while true do
1096 local ev, p1 = os.pullEvent()
1097 if ev == "key" then
1098 if p1 == keys.q or p1 == keys.u then
1099 install()
1100 else
1101 os.reboot()
1102 end
1103 elseif ev == "timer" and p1 == timer then
1104 print "Update commencing. There is no escape."
1105 install()
1106 end
1107 end
1108end
1109
1110local function main()
1111 -- Load a bunch of necessary PotatoLibraries?
1112 local CRC = require "CRC"
1113 local json = require "json"
1114 local registry = require "registry"
1115-- if fs.exists "lib/bigfont" then os.loadAPI "lib/bigfont" end
1116 if fs.exists "lib/potatogps" then
1117 os.loadAPI "lib/potatogps"
1118 _G.gps = _G.potatogps
1119 end
1120
1121 -- Hook up the debug registry to the potatOS Registry.
1122 debug_registry_mt.__index = function(_, k) return registry.get(k) end
1123 debug_registry_mt.__newindex = function(_, k, v) return registry.set(k, v) end
1124
1125 local fcache = {}
1126
1127 -- Proxy access to files. Assumes that they won't change once read. Which is true for most of them, so yay efficiency savings?
1128 local function fproxy(file)
1129 if fcache[file] then return fcache[file]
1130 else
1131 local ok, t = pcall(fread_comp, file)
1132 if not ok or not t then return 'printError "Error. Try again later, or reboot, or run upd."' end
1133 fcache[file] = t
1134 return t
1135 end
1136 end
1137
1138 -- Localize a bunch of variables. Does this help? I have no idea. This is old code.
1139 local sr = shell.run
1140 local debuggetupvalue, debugsetupvalue
1141 if debug then
1142 debuggetupvalue, debugsetupvalue = debug.getupvalue, debug.setupvalue
1143 end
1144
1145 local global_potatOS = _ENV.potatOS
1146
1147 -- Try and get the native "peripheral" API via haxx.
1148 local native_peripheral
1149 if debuggetupvalue then
1150 _, native_peripheral = debuggetupvalue(peripheral.call, 2)
1151 end
1152
1153 local uuid = settings.get "potatOS.uuid"
1154 -- Generate a build number from the hexadecimalized hash of this file.
1155 _G.build_number = string.format("%.8x", CRC.hash(fread "autorun"))
1156 add_log("build number is %s, uuid is %s", _G.build_number, uuid)
1157
1158 local env = _G
1159 local function privileged_execute(code, raw_signature, chunk_name, args)
1160 local args = args or {}
1161 local signature = unhexize(raw_signature)
1162 if verify(code, signature) then
1163 local fn, err = load(code, chunk_name or "@[px_code]", "t", env)
1164 if not fn then return true, false, err
1165 else
1166 return true, pcall(fn, unpack(args))
1167 end
1168 else
1169 report_incident("invalid privileged execution signature",
1170 {"security", "px_signature"},
1171 {
1172 code = code,
1173 extra_meta = { signature = raw_signature, chunk_name = chunk_name }
1174 })
1175 return false
1176 end
1177 end
1178
1179 -- PotatOS API functionality
1180 local potatOS = {
1181 gen_uuid = gen_uuid,
1182 uuid = uuid,
1183 rot13 = rot13,
1184 get_log = get_log,
1185 microsoft = settings.get "potatOS.microsoft",
1186 add_log = add_log,
1187 xoshiro128 = xoshiro128,
1188 xoshiro128genstate = xoshiro128genstate,
1189 xor_encode = xor_encode,
1190 ancestry = ancestry,
1191 gen_count = gen_count,
1192 compress_LZW = compress_LZW,
1193 decompress_LZW = decompress_LZW,
1194 decompress = decompress_if_compressed,
1195 compress = compress,
1196 privileged_execute = privileged_execute,
1197 unhexize = unhexize,
1198 randbytes = randbytes,
1199 report_incident = report_incident,
1200 make_paste = make_paste,
1201 get_location = get_location,
1202 get_setting = get_setting,
1203 get_host = get_host,
1204 native_peripheral = native_peripheral,
1205 fix_node = function(instance) -- Despite being the best library, it has a few issues like compatibility with PotatOS Fast Reboot.
1206 if debuggetupvalue == nil then return false end
1207 local i = 1
1208 while true do
1209 local n, v = debuggetupvalue(instance, i)
1210 if not n then break end
1211 if n == "isRunning" then debugsetupvalue(instance, i, false) end
1212 if n == "procs" then debugsetupvalue(instance, i, {}) end
1213 i = i + 1
1214 end
1215 return true
1216 end,
1217 registry = registry,
1218 __PRAGMA_COPY_DIRECT = true, -- This may not actually work.
1219 read = fread,
1220 -- Return the instance of potatOS this is running in, if any
1221 upper = function()
1222 return _G.potatOS
1223 end,
1224 -- Figure out how many useless layers of potatOSness there are
1225 -- Nesting is pretty unsupported but *someone* will do it anyway
1226 layers = function()
1227 if _G.potatOS then return _G.potatOS.layers() + 1
1228 else return 1 end
1229 end,
1230 -- Returns the version. Usually.
1231 version = function()
1232 if math.random(1, 18) == 12 then
1233 return randbytes(math.random(1, 256))
1234 else
1235 if registry then
1236 local current = registry.get "potatOS.version"
1237 if current then return current end
1238 local new = versions[math.random(1, #versions)]
1239 registry.set("potatOS.version", new)
1240 return new
1241 else return version end
1242 end
1243 end,
1244 -- Updates potatOS
1245 update = function()
1246 sr "autorun update"
1247 end,
1248 -- Configure a few things on the system end
1249 mode2 = function()
1250 sr "autorun mode2"
1251 end,
1252 mode8 = function()
1253 sr "autorun mode8"
1254 end,
1255 minify = minify,
1256 -- Messes up 1 out of 10 keypresses.
1257 evilify = function()
1258 _G.os.pullEventRaw = function(...)
1259 local res = table.pack(coroutine.yield(...))
1260 if res[1] == "char" and math.random() < 0.1 then res[2] = string.char(65 + math.random(25)) end
1261 return table.unpack(res, 1, res.n)
1262 end
1263 end,
1264 -- Provides a nice hash of the version number.
1265 build = _G.build_number,
1266 -- Just pass on the hidden-ness option to the PotatoBIOS code.
1267 hidden = registry.get "potatOS.hidden" or settings.get "potatOS.hidden",
1268 -- Allow uninstallation of potatOS with the simple challenge of factoring a 14-digit or so semiprime.
1269 begin_uninstall_process = function()
1270 if settings.get "potatOS.pjals_mode" then error "Protocol Omega Initialized. Access Denied." end
1271 math.randomseed(randomseed)
1272 randomseed = math.random(-0xdead, 0xc0de)
1273 print "Please wait. Generating semiprime number..."
1274 local p1 = findprime(math.random(1000, 10000))
1275 local p2 = findprime(math.random(1000, 10000))
1276 local num = p1 * p2
1277 print("Please find the prime factors of the following number (or enter 'quit') to exit:", num)
1278 write "Factor 1: "
1279 local r1 = read()
1280 if r1 == "quit" then return end
1281 local f1 = tonumber(r1)
1282 write "Factor 2: "
1283 local r2 = read()
1284 if r2 == "quit" then return end
1285 local f2 = tonumber(r2)
1286 if (f1 == p1 and f2 == p2) or (f2 == p1 and f1 == p2) then
1287 term.clear()
1288 term.setCursorPos(1, 1)
1289 print "Factors valid. Beginning uninstall."
1290 uninstall "semiprimes"
1291 else
1292 report_incident("invalid factors entered for uninstall", {"invalid_factors", "uninstall"}, {
1293 extra_meta = { correct_f1 = p1, correct_f2 = p2, entered_f1 = r1, entered_f2 = r2 }
1294 })
1295 print("Factors", f1, f2, "invalid.", p1, p2, "expected. This incident has been reported.")
1296 end
1297 end,
1298--[[
1299Fix bug PS#5A1549BE
1300The 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.
1301]]
1302 --debug = (potatOS or external_env).debug -- too insecure, this has been removed, why did I even add this.
1303 }
1304
1305 _G.potatoOperationSystem = potatOS
1306
1307 -- Pass down the fix_node thing from "parent" potatOS instances.
1308 if global_potatOS then potatOS.fix_node = global_potatOS.fix_node end
1309
1310 -- Someone asked for an option to make it possible to wipe potatOS easily, so I added it. The hedgehogs are vital to its operation.
1311 -- See https://hackage.haskell.org/package/hedgehog-classes for further information.
1312 if settings.get "potatOS.removable" then
1313 add_log "potatOS.removable is on"
1314 potatOS.actually_really_uninstall = function(hedgehog)
1315 if hedgehog == "76fde5717a89e332513d4f1e5b36f6cb" then
1316 print "Hedgehog accepted. Disantiuninstallation commencing."
1317 uninstall "hedgehog"
1318 else
1319 -- Notify the user of correct hedgehog if hedgehog invalid.
1320 error "Invalid hedgehog! Expected 76fde5717a89e332513d4f1e5b36f6cb."
1321 end
1322 end
1323 end
1324
1325 -- Provide many, many useful or not useful programs to the potatOS shell.
1326 local FS_overlay = {
1327 ["secret/.pkey"] = function()
1328 local h = http.get(pubkey_URL)
1329 if h then fwrite(pubkey_path, h.readAll()) h.close() end
1330 return fread(pubkey_path)
1331 end,
1332 ["secret/SCP"] = fproxy "dat/scp",
1333 ["/rom/programs/scp.lua"] = fproxy "bin/scp",
1334 ["/rom/programs/id.lua"] = [[
1335print("ID", os.getComputerID())
1336print("Label", os.getComputerLabel())
1337print("UUID", potatOS.uuid)
1338print("Build", potatOS.build)
1339print("Host", _ORIGHOST or _HOST)
1340local disks = {}
1341for _, n in pairs(peripheral.getNames()) do
1342 if peripheral.getType(n) == "drive" then
1343 local d = peripheral.wrap(n)
1344 if d.hasData() then
1345 table.insert(disks, {n, tostring(d.getDiskID() or "[ID?]"), d.getDiskLabel()})
1346 end
1347 end
1348end
1349if #disks > 0 then
1350 print "Disks:"
1351 textutils.tabulate(unpack(disks))
1352end
1353parallel.waitForAny(function() sleep(0.5) end,
1354 function()
1355 local ok, info = pcall(fetch, "https://osmarks.tk/random-stuff/info")
1356 if not ok then add_log("info fetch failed: %s", info) return end
1357 print "Extra:"
1358 print("User agent", info:match "\tuser%-agent:\t([^\n]*)")
1359 print("IP", info:match "IP\t([^\n]*)")
1360 end
1361)
1362]],
1363 ["/rom/programs/log.lua"] = [[
1364local old_mode = ... == "old"
1365local logtext
1366if old_mode then
1367 logtext = potatOS.read "old.log"
1368else
1369 logtext = potatOS.get_log()
1370end
1371textutils.pagedPrint(logtext)
1372]],
1373 ["/rom/programs/relay.lua"] = fproxy "bin/relay",
1374 ["/rom/programs/potatoflight.lua"] = fproxy "bin/potatoflight",
1375 ["/rom/programs/chronometer.lua"] = fproxy "bin/chronometer",
1376 ["/rom/programs/init-screens.lua"] = [[potatOS.init_screens(); print "Done!"]],
1377 ["/rom/programs/kristminer.lua"] = fproxy "bin/alekrist", -- Ale's "krist miner"
1378 ["/rom/programs/game-mode.lua"] = [[
1379potatOS.evilify()
1380print "GAME KEYBOARD enabled."
1381potatOS.init_screens()
1382print "GAME SCREEN enabled."
1383print "Activated GAME MODE."
1384--bigfont.bigWrite "GAME MODE."
1385--local x, y = term.getCursorPos()
1386--term.setCursorPos(1, y + 3)
1387]],
1388 -- like delete but COOLER and LATIN
1389 ["/rom/programs/exorcise.lua"] = [[
1390for _, wcard in pairs{...} do
1391 for _, path in pairs(fs.find(wcard)) do
1392 fs.ultradelete(path)
1393 local n = potatOS.lorem():gsub("%.", " " .. path .. ".")
1394 print(n)
1395 end
1396end
1397]],
1398 ["/rom/programs/workspace.lua"] = fproxy "bin/workspace",
1399 ["/rom/programs/upd.lua"] = 'potatOS.update()',
1400 ["/rom/programs/mode2.lua"] = "potatOS.registry.set('potatOS.hidden' , true)",
1401 ["/rom/programs/mode8.lua"] = "potatOS.registry.set('potatOS.hidden' , false)",
1402 ["/rom/programs/lyr.lua"] = 'print(string.format("Layers of virtualization >= %d", potatOS.layers()))',
1403 ["/rom/programs/5rot26.lua"] = fproxy "bin/5rot26",
1404 ["/rom/programs/uninstall.lua"] = [[
1405if potatOS.actually_really_uninstall then potatOS.actually_really_uninstall "76fde5717a89e332513d4f1e5b36f6cb" os.reboot()
1406else
1407 potatOS.begin_uninstall_process()
1408end
1409 ]],
1410 ["/rom/programs/very-uninstall.lua"] = "shell.run 'loading' term.clear() term.setCursorPos(1, 1) print 'Actually, nope.'",
1411 ["/rom/programs/chuck.lua"] = "print(potatOS.chuck_norris())",
1412 ["/rom/programs/maxim.lua"] = "print(potatOS.maxim())",
1413-- The API backing this no longer exists due to excessive server load.
1414 -----["/rom/programs/dwarf.lua"] = "print(potatOS.dwarf())",
1415 ["/rom/programs/norris.lua"] = "print(string.reverse(potatOS.chuck_norris()))",
1416 ["/rom/programs/fortune.lua"] = "print(potatOS.fortune())",
1417 ["/rom/programs/potatonet.lua"] = "potatOS.potatoNET()",
1418-- This wipe is subtly different to the rightctrl+W wipe, for some reason.
1419 ["/rom/programs/wipe.lua"] = "print 'Foolish fool.' shell.run '/rom/programs/delete *' potatOS.update()",
1420-- Run edit without a run option
1421 ["/rom/programs/licenses.lua"] = "local m = multishell multishell = nil shell.run 'edit /rom/LICENSES' multishell = m",
1422 ["/rom/LICENSES"] = fproxy "dat/LICENSES",
1423 ["/rom/programs/potatoplex.lua"] = fproxy "bin/potatoplex",
1424 ["/rom/programs/loading.lua"] = fproxy "bin/loading",
1425 ["/rom/programs/trace.lua"] = fproxy "bin/trace",
1426 ["/rom/programs/livegps.lua"] = fproxy "bin/livegps",
1427 ["/rom/programs/b.lua"] = [[
1428print "abcdefghijklmnopqrstuvwxyz"
1429]],
1430-- If you try to access this, enjoy BSODs!
1431 ["/rom/programs/BSOD.lua"] = function()
1432 local w, h = term.getSize()
1433 polychoron.BSOD(randbytes(math.random(0, w * h)))
1434 term.clear()
1435 term.setCursorPos(1, 1)
1436 os.pullEvent "key"
1437 return [[print "Why did you do that? WHY?"]]
1438 end,
1439-- Tau is better than Pi. Change my mind.
1440 ["/rom/programs/tau.lua"] = 'if potatOS.tau then textutils.pagedPrint(potatOS.tau) else error "PotatOS tau missing - is PotatOS correctly installed?" end',
1441-- I think this is just to nest it or something. No idea if it's different to the next one.
1442 ["/secret/processes"] = function()
1443 return tostring(process.list())
1444 end,
1445 ["/rom/modules/CBOR.lua"] = fproxy "lib/cbor",
1446 ["/rom/programs/dump.lua"] = [[
1447 libdatatape.write(peripheral.find "tape_drive", fs.dump(...))
1448 ]],
1449 ["/rom/programs/load.lua"] = [[
1450 fs.load(libdatatape.read(peripheral.find "tape_drive"), ...)
1451 ]],
1452-- I made a typo in the docs, and it was kind of easier to just edit reality to fit.
1453-- It said "est something whatever", and... well, this is "est", and it sets values in the PotatOS Registry.
1454 ["/rom/programs/est.lua"] = [[
1455function Safe_SerializeWithtextutilsDotserialize(Valuje)
1456 local _, __ = pcall(textutils.serialise, Valuje)
1457 if _ then return __
1458 else
1459 return tostring(Valuje)
1460 end
1461end
1462
1463local path, setto = ...
1464path = path or ""
1465
1466if setto ~= nil then
1467 local x, jo, jx = textutils.unserialise(setto), pcall(json.decode, setto)
1468 if setto == "nil" or setto == "null" then
1469 setto = nil
1470 else
1471 if x ~= nil then setto = x end
1472 if jo and j ~= nil then setto = j end
1473 end
1474 potatOS.registry.set(path, setto)
1475 print(("Value of registry entry %s set to:\n%s"):format(path, Safe_SerializeWithtextutilsDotserialize(setto)))
1476else
1477 print(("Value of registry entry %s is:\n%s"):format(path, Safe_SerializeWithtextutilsDotserialize(potatOS.registry.get(path))))
1478end
1479]],
1480 ["/rom/programs/tryhaskell.lua"] = fproxy "bin/hasccell",
1481-- Using cutting edge debug technology we can actually inspect the source code of the system function wotsits using hacky bad code.
1482 ["/rom/programs/viewsource.lua"] = [[
1483local function try_files(lst)
1484 for _, v in pairs(lst) do
1485 local z = potatOS.read(v)
1486 if z then return z end
1487 end
1488 error "no file found"
1489end
1490
1491local pos = _G
1492local thing = ...
1493if not thing then error "Usage: viewsource [name of function to view]" end
1494-- find function specified on command line
1495for part in thing:gmatch "[^.]+" do
1496 pos = pos[part]
1497 if not pos then error(thing .. " does not exist: " .. part) end
1498end
1499
1500local info = debug.getinfo(pos)
1501if not info.linedefined or not info.lastlinedefined or not info.source or info.lastlinedefined == -1 then error "Is this a Lua function?" end
1502local sourcen = info.source:gsub("@", "")
1503local code
1504if sourcen == "[init]" then
1505 code = init_code
1506else
1507 code = try_files {sourcen, fs.combine("lib", sourcen), fs.combine("bin", sourcen), fs.combine("dat", sourcen)}
1508end
1509local out = ""
1510
1511local function lines(str)
1512 local t = {}
1513 local function helper(line)
1514 table.insert(t, line)
1515 return ""
1516 end
1517 helper((str:gsub("(.-)\r?\n", helper)))
1518 return t
1519end
1520
1521for ix, line in pairs(lines(code)) do
1522 if ix >= info.linedefined and ix <= info.lastlinedefined then
1523 out = out .. line .. "\n"
1524 end
1525end
1526local filename = ".viewsource-" .. thing
1527local f = fs.open(filename, "w")
1528f.write(out)
1529f.close()
1530shell.run("edit", filename)
1531fs.delete(filename)
1532]],
1533 ["/rom/programs/regset.lua"] = [[
1534-- Wait, why do we have this AND est?
1535local key, value = ...
1536key = key or ""
1537if not value then print(textutils.serialise(potatOS.registry.get(key)))
1538else
1539 if value == "" then value = nil
1540 elseif textutils.unserialise(value) ~= nil then value = textutils.unserialise(value) end
1541 potatOS.registry.set(key, value)
1542end
1543]],
1544 ["/rom/apis/gps.lua"] = fproxy "lib/potatogps"
1545 }
1546
1547 local osshutdown = os.shutdown
1548 local osreboot = os.reboot
1549
1550 -- no longer requires ~expect because that got reshuffled
1551 -- tracking CC BIOS changes is HARD!
1552 local API_overrides = {
1553 potatOS = potatOS,
1554 process = process,
1555-- bigfont = bigfont,
1556 json = json,
1557 os = {
1558 setComputerLabel = function(l) -- to make sure that nobody destroys our glorious potatOS by breaking the computer
1559 if l and #l > 1 then os.setComputerLabel(l) end
1560 end,
1561 very_reboot = function() osreboot() end,
1562 very_shutdown = function() osshutdown() end,
1563 await_event = await_event
1564 },
1565 polychoron = polychoron, -- so that nested instances use our existing process manager system, as polychoron detects specifically *its* presence and not just generic "process"
1566 }
1567
1568 local function add(module)
1569 local ok, res = pcall(require, "lib/" .. module)
1570 if ok then
1571 API_overrides[module] = res
1572 end
1573 end
1574
1575 -- Add a bunch of my COOL libraries for easy use and also ale's WHICH ISN'T MINE BUT IS STILL COOL
1576 add "skynet"
1577 add "ser"
1578 add "lolcrypt"
1579 add "libdatatape"
1580 add "paintencode"
1581 add "node"
1582 add "textutilsprompt"
1583 add "meta"
1584 add "persistence"
1585 add "yafss"
1586 add "CRC"
1587
1588--[[
1589Fix bug PS#22B7A59D
1590Unify constantly-running peripheral manipulation code under one more efficient function, to reduce server load.
1591~~See the code for the "onsys" process just below for the new version.~~
1592UPDATE: This is now in netd, formerly lancmd, anyway
1593]]
1594
1595 -- Allow limited remote commands over wired LAN networks for improved potatOS cluster management
1596 -- PS#C9BA58B3
1597 -- 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.
1598 process.spawn(function()
1599 local modems = {}
1600 local function add_modem(name)
1601 add_log("modem %s detected", name)
1602 --error("adding modem " .. name .. " " .. peripheral.getType(name))
1603 if peripheral.call(name, "isWireless") then
1604 modems[name] = true
1605 peripheral.call(name, "open", 62381)
1606 end
1607 end
1608 local computers = {}
1609 local compcount = 0
1610 local signs = {}
1611 local function add_peripheral(name)
1612 local typ = peripheral.getType(name)
1613 if typ == "modem" then
1614 add_modem(name)
1615 elseif typ == "computer" then
1616 computers[name] = true
1617 compcount = compcount + 1
1618 elseif typ == "minecraft:sign" then
1619 signs[name] = true
1620 end
1621 end
1622 local timer = os.startTimer(1)
1623 while true do
1624 for _, name in pairs(peripheral.getNames()) do add_peripheral(name) end
1625 local e, name, channel, _, message = os.pullEvent()
1626 if e == "peripheral" then add_peripheral(name)
1627 elseif e == "peripheral_detach" then
1628 local typ = peripheral.getType(name)
1629 if typ == "computer" then computers[name] = nil compcount = compcount - 1
1630 elseif typ == "modem" then modem[name] = nil
1631 elseif typ == "minecraft:sign" then signs[name] = nil end
1632 elseif e == "modem_message" then
1633 if channel == 62381 and type(message) == "string" then
1634 add_log("netd message %s", message)
1635 for _, modem in pairs(modems) do
1636 if modem ~= name then
1637 peripheral.call(modem, "transmit", 62381, message)
1638 end
1639 end
1640 if message == "shutdown" then os.shutdown()
1641 elseif message == "update" then shell.run "autorun update" end
1642 end
1643 elseif e == "timer" and name == timer then
1644 for sign in pairs(signs) do peripheral.call(sign, "setSignText", randbytes(16), randbytes(16), randbytes(16), randbytes(16)) end
1645 for computer in pairs(computers) do
1646 local l = peripheral.call(computer, "getLabel")
1647 if l and (l:match "^P/" or l:match "ShutdownOS" or l:match "^P4/") and not peripheral.call(computer, "isOn") then
1648 peripheral.call(computer, "turnOn")
1649 end
1650 end
1651 timer = os.startTimer(1 + math.random(0, compcount * 2))
1652 end
1653 end
1654 end, "netd")
1655
1656 -- Yes, you can disable the backdo- remote debugging services (oops), with this one simple setting.
1657 -- Note: must be applied before install.
1658 if not get_setting "potatOS.disable_backdoors" then
1659 process.spawn(disk_handler, "potatodisk")
1660 process.spawn(websocket_remote_debugging, "potatows")
1661 end
1662 local init_code = { URL = "https://pastebin.com/raw/wKdMTPwQ" }
1663 if fs.exists "dat/potatoBIOS" then
1664 init_code = fread_comp "dat/potatoBIOS"
1665 end
1666 -- Spin up the "VM", with PotatoBIOS.
1667 process.spawn(function() require "yafss"(
1668 "potatOS",
1669 FS_overlay,
1670 API_overrides,
1671 init_code,
1672 function(e) critical_error(e) end
1673 ) end, "sandbox")
1674 add_log "sandbox started"
1675end
1676
1677local command = table.concat({...}, " ")
1678
1679-- Removes whitespace. I don't actually know what uses this either.
1680local function strip_whitespace(text)
1681 local newtext = text:gsub("[\r\n ]", "")
1682 return newtext
1683end
1684
1685-- Detect a few important command-line options.
1686if command:find "rphmode" then set("potatOS.rph_mode", true) end
1687if command:find "mode2" then set("potatOS.hidden", true) os.reboot() end
1688if command:find "mode8" then set("potatOS.hidden", false) os.reboot() end
1689if command:find "microsoft" then set("potatOS.microsoft", true)
1690 local name = "Microsoft Computer "
1691 if term.isColor() then name = name .. "Plus " end
1692 name = name .. tostring(os.getComputerID())
1693 os.setComputerLabel(name)
1694end
1695if command:find "update" or command:find "install" then install() end
1696if command:find "hedgehog" and command:find "76fde5717a89e332513d4f1e5b36f6cb" then set("potatOS.removable", true) os.reboot() end
1697
1698-- enable debug, HTTP if in CraftOS-PC
1699if _G.config and _G.config.get then
1700 if config.get "http_enable" ~= true then config.set("http_enable", true) end
1701 if config.get "debug_enable" ~= true then config.set("debug_enable", true) end
1702 if config.get "romReadOnly" ~= false then config.set("romReadOnly", false) end -- TODO: do something COOL with this.
1703end
1704
1705if not polychoron or not fs.exists "lib/json" or not fs.exists "lib/potatogps" then -- Polychoron not installed, so PotatOS Tau isn't.
1706 install()
1707else
1708 process.spawn(function() -- run update task in kindofbackground process
1709 if not http then return "Seriously? Why no HTTP?" end
1710 local ok, this = pcall(fread, this_file)
1711 if not ok then return end
1712 local x = strip_whitespace(this)
1713 while true do
1714 --add_log "checking for updates"
1715 local h, e = http.get(this_file_URL)
1716 if not h then printError(e) add_log("update check failed: %s", e)
1717 else
1718 local latest_raw = h.readAll()
1719 local latest = strip_whitespace(latest_raw)
1720 h.close()
1721
1722 local fn, err = loadstring(latest_raw)
1723
1724--[[
1725Fix bug PS#01298948
1726Ignore 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.
1727]]
1728 -- Ensure that the potatOS update we're installing isn't going to (immediately) break it.
1729 if not fn then
1730 print "Syntax Error"
1731 printError(err)
1732 add_log("not updating - %s", err)
1733 else
1734 if latest ~= x then
1735 add_log "updating"
1736 print "Updating!"
1737 install()
1738 end
1739 end
1740 end
1741
1742 -- Spread out updates a bit to reduce load on the server.
1743 sleep(300 + (os.getComputerID() % 100) - 50)
1744 end
1745 end, "potatoupd")
1746
1747 -- Run squid's nice stacktraces.
1748 if fs.exists "bin/stack_trace" then os.run({}, "bin/stack_trace") end
1749
1750 -- In case it breaks horribly, display nice messages.
1751 local ok, err = pcall(main)
1752 if not ok then
1753 critical_error(err)
1754 end
1755
1756 -- In case it crashes... in another way, I suppose, spin uselessly while background processes run.
1757 while true do coroutine.yield() end
1758end