· 6 years ago · Mar 29, 2020, 04:56 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- frogs
36or any other issue caused directly or indirectly due to use of this product.
37
38Best viewed in Internet Explorer 6.00000000000004 running on a Difference Engine emulated under MacOS 7 on a Pentium 3.
39
40Features:
41- Fortunes/Dwarf Fortress output (UPDATE: no longer available)/Chuck Norris jokes on boot (wait, IS this a feature?)
42- (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
43- Skynet (rednet-ish stuff over websocket to my server) and Lolcrypt (encoding data as lols and punctuation) built in for easy access!
44- Convenient OS-y APIs - add keyboard shortcuts, spawn background processes & do "multithreading"-ish stuff.
45- Great features for other idio- OS designers, like passwords and fake loading (est potatOS.stupidity.loading [time], est potatOS.stupidity.password [password]).
46- Digits of Tau available via a convenient command ("tau")
47- Potatoplex and Loading built in ("potatoplex"/"loading") (potatoplex has many undocumented options)!
48- Stack traces (yes, I did steal them from MBS)
49- Backdoors- er, remote debugging access (it's secured, via ECC signing on disks and websocket-only access requiring a key for the other one)
50- All this useless random junk can autoupdate (this is probably a backdoor)!
51- EZCopy allows you to easily install potatOS on another device, just by sticking it in the disk drive of any potatOS device!
52- fs.load and fs.dump - probably helpful somehow.
53- Blocks bad programs (like the "Webicity" browser).
54- Fully-featured process manager. Very fully-featured. No existing code uses most of the features.
55- Can run in "hidden mode" where it's at least not obvious at a glance that potatOS is installed.
56- Connects to SPUDNET.
57- Convenient, simple uninstall with the "uninstall" command.
58- Turns on any networked potatOS computers!
59- Edits connected signs to use as ad displays.
60- A recycle bin.
61- An exorcise command, which is like delete but better.
62- Support for a wide variety of Lorem Ipsum.
63- The PotatOS Registry - Like the Windows one, but better. Edit its contents with "est" (that is not a typo'd "set").
64- A window manager. It's bundled, at least. Not actually *tested*. Like most of the bundled programs.
65- 5rot26 encryption program.
66- A license information viewing program!
67- "b", a command to print the alphabet.
68- A command to view the source of any potatOS function.
69- Advanced sandboxing prevents malicious programs from removing potatOS.
70- Reimplements the string metatable bug!
71- A frontend for tryhaskell.org - yes, really...
72- Groundbreaking new PotatOS Incident Reports system to report incidents to potatOS.
73- Might be GDPR-compliant!
74- Reimplements half of the CC BIOS because it's *simpler* than the alternative!
75- Contains between 0 and 1041058 exploits. Estimation of more precise values is still in progress.
76
77Please note that under certain circumstances, the potatOS networking subsystem may control God.
78
79Copyright 2019 osmarks/gollark
80
81Permission 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:
82
83The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
84
85THE 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.
86
87I 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.
88
89Did 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.
90
91This license also extends to other PotatOS components or bundled software owned by me.
92]]
93
94if settings.get "potatOS.rph_mode" == true then
95 print "PotatOS Rph Compliance Mode: Enabled."
96 return false end
97
98--[[
99Server Policy Framework
100On 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).
101Since 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.
102]]
103local SPF = {
104 server_policy = {
105 switchcraft = {
106 ["potatOS.disable_ezcopy"] = true
107 }
108 },
109 server = nil
110}
111
112os.pullEvent = os.pullEventRaw
113
114local function get_registry_raw(name)
115 return require "registry".get(name)
116end
117
118local function get_registry(name)
119 local ok, res = pcall(get_registry_raw, name)
120 if not ok then return nil end
121 return res
122end
123
124-- Get a setting - uses the CC native settings API, the registry, and if nothing is specified the SPF setting
125local function get_setting(name)
126 local cc_setting = settings.get(name)
127 local reg_setting = get_registry(name)
128 local SPF_setting
129 if SPF.server and SPF.server_policy[SPF.server] and not get_registry "potatOS.disable_SPF" then
130 SPF_setting = SPF.server_policy[SPF.server][name]
131 end
132 if cc_setting ~= nil then return cc_setting
133 elseif reg_setting ~= nil then return reg_setting
134 elseif SPF_setting ~= nil then return SPF_setting end
135end
136
137-- Detect SC for the SPF
138if _G.switchcraft then SPF.server = "switchcraft" end
139
140-- print things to console for some reason? but only in CCEmuX
141--[[
142if ccemux and ccemux.echo then
143 local function echo_many(...)
144 local strings = {}
145 for _, v in pairs {...} do
146 table.insert(strings, tostring(v))
147 end
148 ccemux.echo(table.concat(strings, " "))
149 end
150
151 local oldprint = print
152 function print(...)
153 oldprint(...)
154 pcall(echo_many, ...)
155 end
156
157 local olderror = error
158 function error(x, ...)
159 olderror(x, ...)
160 pcall(echo_many, "[ERR]", x)
161 end
162end
163]]
164
165_G.os.pullEvent = coroutine.yield
166
167--[[
168Fix bug PS#85DAA5A8
169The `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
170]]
171-- Like pullEvent, but cooler.
172function _G.os.await_event(filter)
173 while true do
174 local ev = {coroutine.yield(filter)}
175 if ev[1] ~= "terminate" or filter == nil or ev[1] == filter then
176 return unpack(ev)
177 end
178 end
179end
180
181--[[
182Fix bug PS#7C8125D6
183By 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.
184]]
185local randomseed = math.random(-0xdead, 0xc0de)
186
187local version = "TuberOS"
188
189term.clear()
190term.setCursorBlink(false)
191
192-- Utility functions and stuff
193
194-- 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.
195-- Steve, this seems exploitable, it's going.
196-- What? How is it meant to work nestedly? - Steve
197--[[
198Fix bug PS#334CEB26
199This has so many problems... not just sandbox escapes but weird duplicated and missing events. Why did I add this?!
200]]
201--[[
202local httpwebsocket = http.websocket
203local socket_cache = {}
204function http.websocket(URL)
205 if socket_cache[URL] then return socket_cache[URL]
206 else
207 socket_cache[URL] = httpwebsocket(URL)
208 return socket_cache[URL]
209 end
210end]]
211
212-- SquidDev has told me of `debug.getregistry`, so I decided to implement it.
213local debug_registry_mt = {}
214local debug_registry = setmetatable({}, debug_registry_mt)
215
216if debug then
217 function debug.getregistry()
218 return debug_registry
219 end
220end
221
222-- Maeks a paste on pastebin, obviously
223local function make_paste(name, content)
224 -- CC's default API key
225 local key = "0ec2eb25b6166c0c27a394ae118ad829"
226 local response, e = http.post(
227 "https://pastebin.com/api/api_post.php",
228 "api_option=paste&" ..
229 "api_dev_key=" .. key .. "&" ..
230 "api_paste_format=lua&" ..
231 "api_paste_name=" .. textutils.urlEncode(name) .. "&" ..
232 "api_paste_code=" .. textutils.urlEncode(content)
233 )
234 if not response then error(e)
235 else
236 local r = response.readAll()
237 return string.match(r, "[^/]+$")
238 end
239end
240
241-- Converts a hex-format signature to a nonhex one
242local function unhexize(key)
243 local out = {}
244 for i = 1, #key, 2 do
245 local pair = key:sub(i, i + 1)
246 table.insert(out, tonumber(pair, 16))
247 end
248 return out
249end
250
251-- Checks if a number is prime. You would never guess it did that. You should thank me for being so helpful.
252function _G.isprime(n)
253 for i = 2, math.sqrt(n) do
254 if n % i == 0 then return false end
255 end
256 return true
257end
258
259-- Finds the first prime number after "from". Look at that really complex code.
260function _G.findprime(from)
261 local i = from
262 while true do
263 if isprime(i) then return i end
264 i = i + 1
265 end
266end
267
268-- 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.
269local function copy(tabl)
270 local new = {}
271 for k, v in pairs(tabl) do
272 if type(v) == "table" and v ~= tabl then
273 new[k] = copy(v)
274 else
275 new[k] = v
276 end
277 end
278 return new
279end
280
281-- Generates "len" random bytes (why no unicode, dan200?!)
282local function randbytes(len)
283 local out = ""
284 for i = 1, len do
285 out = out .. string.char(math.random(0, 255))
286 end
287 return out
288end
289
290-- Write "c" to file "n"
291local function fwrite(n, c)
292 local f = fs.open(n, "w")
293 f.write(c)
294 f.close()
295end
296
297-- Read file "n"
298local function fread(n)
299 local f = fs.open(n, "r")
300 local out = f.readAll()
301 f.close()
302 return out
303end
304
305-- Set key in .settings
306local function set(k, v)
307 settings.set(k, v)
308 settings.save(".settings")
309end
310
311-- 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.
312local external_env = copy(_G)
313if not external_env.shell then external_env.shell = shell end
314-- Block termination. Maybe?!
315external_env.os.pullEvent = external_env.os.pullEventRaw
316
317-- Checks that "sig" is a valid signature for "data" (i.e. signed with the potatOS master key). Used for disk and formerly tape verification.
318local function verify(data, sig)
319 local pkey = textutils.unserialise(fread "dat/.pkey")
320 local ecc = require "./ecc"
321 local e = ecc "ecc"
322 local ok, res = pcall(e.verify, pkey, data, sig)
323 print("ERR:", not ok, "\nRES:", res)
324 return ok and res
325end
326
327-- Spawn a background process to update location every minute
328local location
329if process then
330 process.spawn(function()
331 local m = peripheral.find("modem", function(_, p) return p.isWireless() end)
332 if not m then return "no modem" end
333 while true do
334 local x, y, z, dim = gps.locate()
335 if x then
336 location = {x, y, z, dim}
337 end
338 sleep(60)
339 end
340 end, "locationd")
341end
342
343-- Just a function to get the locationd-gotten location so it can be provided in the potatOS environment
344local function get_location()
345 return unpack(location)
346end
347
348-- Get data which is probably sufficient to uniquely identify a computer on a server.
349function _G.get_host()
350 local out = {
351 label = os.getComputerLabel(),
352 ID = os.getComputerID(),
353 lua_version = _VERSION,
354 CC_host = _HOST,
355 build = _G.build_number,
356 craftOS_version = os.version(),
357 debug_available = _G.debug ~= nil,
358 ingame_location = location,
359 SPF_server = SPF.server,
360 CC_default_settings = _CC_DEFAULT_SETTINGS,
361 }
362 if _G.debug then out.stack = debug.traceback() end
363 return out
364end
365
366-- 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.
367--[[
368Fix bug PS#C23E2F6F
369Now actually report... well, some classes of error, definitely some incidents... to help with debugging. Also tracking down of culprits.
370]]
371local function report_incident(incident, notify)
372 local json = require "json"
373 if type(incident) ~= "string" then error "incident description must be string" end
374 http.request("https://osmarks.tk/wsthing/report", json.encode { report = incident, host = get_host() }, {["content-type"] = "application/json"})
375 if notify then print "This incident has been reported." end
376end
377
378-- Upgrade other disks to contain potatOS and/or load debug programs (mostly the "OmniDisk") off them.
379local function process_disk(disk_side)
380 local mp = disk.getMountPath(disk_side)
381 if not mp then return end
382 local ds = fs.combine(mp, "startup") -- Find paths to startup and signature files
383 local disk_ID = disk.getID(disk_side)
384 local sig_file = fs.combine(mp, "signature")
385 -- shell.run disks marked with the Brand of PotatOS
386 -- except not actually, it's cool and uses load now
387
388 if fs.exists(ds) and fs.exists(sig_file) then
389 local code = fread(ds)
390 local sig_raw = fread(sig_file)
391 local sig
392 if sig_raw:find "{" then sig = textutils.unserialise(sig_raw)
393--[[
394Fix bug PS#56CB502C
395The 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.
396]]
397 else sig = unhexize(sig_raw) end
398 disk.eject(disk_side)
399 if verify(code, sig) then
400 -- run code, but safely (via pcall)
401 -- print output for debugging
402 print "Signature Valid; PotatOS Disk Loading"
403 local out, err = load(code, "@disk/startup", nil, external_env)
404 if not out then printError(err)
405 else
406 local ok, res = pcall(out, { side = disk_side, mount_path = mp, ID = disk_ID })
407 if ok then
408 print(textutils.serialise(res))
409 else
410 printError(res)
411 end
412 end
413 else
414 printError "Invalid Signature!"
415 printError "Initiating Procedure 5."
416 report_incident(("invalid signature on disk - signature %s code %s"):format(sig_raw, code))
417 printError "This incident has been reported."
418 end
419 -- if they're not PotatOS'd, write it on
420 else
421 if get_setting "potatOS.disable_ezcopy" then return end
422 fs.delete(ds)
423 fwrite(ds, "shell.run 'pastebin run RM13UGFa update --gdpr-compliant=maybe' -- PotatOS, the best OS")
424 end
425end
426
427-- Infect disks when they're put in and on boot
428local function disk_handler()
429 -- I would use peripheral.find, but CC's disk API is weird.
430 -- Detect disks initially
431 for _, n in pairs(peripheral.getNames()) do
432 -- lazily avoid crashing, this is totally fine and not going to cause problems
433 if peripheral.getType(n) == "drive" then
434 local ok, err = pcall(process_disk, n)
435 if not ok then printError(err) end
436 end
437 end
438
439 -- Detect disks as they're put in. Mwahahahaha.
440 -- Please note that this is for definitely non-evil purposes only.
441 while true do
442 local ev, disk_side = os.await_event "disk"
443 local ok, err = pcall(process_disk, disk_side)
444 if not ok then printError(err) end
445 end
446end
447
448--[[
449Fix bug PS#201CA2AA
450Serializing functions, recursive tables, etc. - this is done fairly often - can cause a complete crash of the SPUDNET process. This fixes that.
451]]
452-- Serialize (i.e. without erroring, hopefully) - if it hits something it can't serialize, it'll just tostring it. For some insanely stupid reason CC can send recursive tables over modem, but that's unrelated.
453local function safe_serialize(data)
454 local json = require "json"
455 local ok, res = pcall(json.encode, data)
456 if ok then return res
457 else return json.encode(tostring(data)) end
458end
459
460-- Powered by SPUDNET, the simple way to include remote debugging or backdoors in *your* OS. Contact Gollark today.
461local function websocket_remote_debugging()
462 if not http or not http.websocket then return "Websockets do not actually exist on this platform" end
463
464 local ws = http.websocket "wss://osmarks.tk/wsthing/potatOS"
465
466 if not ws then return end
467
468 local function send(msg)
469 ws.send(safe_serialize(msg))
470 end
471
472 local function recv()
473 return ws.receive()
474 end
475
476 external_env.send = send
477 external_env.recv = recv
478
479 while true do
480 -- Receive and run code which is sent via SPUDNET
481 local code = recv()
482 local f, error = load(code, "@<code>", "t", external_env)
483 if f then -- run safely in background, send back response
484 process.thread(function() local resp = {pcall(f)} send(resp) end, "spudnetexecutor")
485 else
486 send {false, error}
487 end
488 end
489end
490
491-- 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.
492local function is_valid_lua(text)
493 if load(text) then return true
494 else return false end
495end
496
497-- Send code to osmarks.tk minification API to, well, minify it.
498-- Does not actually work any more, but that's fine because it's not used!
499local function minify(code)
500 if not is_valid_lua(code) then return code end
501 local url = "https://osmarks.tk/luamin/" .. math.random(0, 1000000000)
502 http.request(url, code)
503 while true do
504 local event, result_url, handle = os.pullEvent()
505 if event == "http_success" and url == result_url then
506 local text = handle.readAll()
507 handle.close()
508 return text
509 elseif event == "http_failure" and url == result_url then
510 local text = handle.readAll()
511 handle.close()
512 error(text)
513 end
514 end
515end
516
517local this_file_URL = "https://pastebin.com/raw/RM13UGFa"
518-- 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.
519local this_file = "autorun"
520
521--[[
522Fix bug PS#776F98D3
523Files are now organized somewhat neatly on the filesystem. Somewhat.
524]]
525_G.skynet_CBOR_path = "lib/cbor"
526--_G.package = _G.package
527package.path = "/lib/?;/lib/?.lua;" .. package.path
528--[[
529if not _G.require then
530 function _G.require(package)
531 for search_path in string.gmatch(_G.package.path, "[^;]+") do
532 local path = search_path:gsub("?", package)
533 end
534 end
535end]]
536
537-- A mapping of URLs to filenames for components of PotatOS
538local files = {
539 [this_file_URL] = this_file,
540 ["https://pastebin.com/raw/HL0SZhJG"] = "startup",
541 ["https://pastebin.com/raw/Frv3xkB9"] = "lib/yafss",
542 ["https://raw.githubusercontent.com/rxi/json.lua/bee7ee3431133009a97257bde73da8a34e53c15c/json.lua"] = "lib/json",
543 ["https://pastebin.com/raw/wYBZjQhN"] = "bin/potatoplex",
544 ["https://pastebin.com/raw/NdUKJ07j"] = "dat/LICENSES",
545 ["https://pastebin.com/raw/3LfWxRWh"] = "lib/bigfont",
546 ["https://pastebin.com/raw/7f4Zqrh5"] = "lib/chaos",
547 ["https://raw.githubusercontent.com/osmarks/Loading/master/loading.lua"] = "bin/loading",
548 ["https://raw.githubusercontent.com/osmarks/skynet/master/client.lua"] = "lib/skynet",
549 ["https://pastebin.com/raw/Sc0DU3rA"] = "lib/ecc",
550 ["https://pastebin.com/raw/jbmWhp4P"] = "dat/.pkey",
551 ["https://pastebin.com/raw/rxkE8N8b"] = "bin/stack_trace",
552 ["https://pastebin.com/raw/EGPpcZbN"] = "lib/lolcrypt",
553 ["https://pastebin.com/raw/eR4RfSiT"] = "lib/libdatatape",
554 ["https://pastebin.com/raw/t4n65sEk"] = "lib/paintencode",
555 ["https://pastebin.com/raw/E7x5ZLSY"] = "bin/hasccell", -- yes I made a haskell ~~interpreter~~ web compiler thing frontend; don't judge me
556 ["https://pastebin.com/raw/yEwXxHkX"] = "lib/CRC",
557 ["https://pastebin.com/raw/2kRenvr3"] = "lib/registry",
558 ["https://pastebin.com/raw/KXHSsHkt"] = "lib/ser",
559 ["https://raw.githubusercontent.com/Ale32bit-CC/Node.lua/master/node.lua"] = "lib/node", -- the best library
560 ["https://pastebin.com/raw/3NVepHYu"] = "lib/textutilsprompt",
561 ["https://pastebin.com/raw/v4Ge7umh"] = "lib/meta",
562 ["https://pastebin.com/raw/jE4guV48"] = "lib/persistence",
563 ["https://pastebin.com/raw/DKriPmPe"] = "lib/alekrist",
564 ["https://pastebin.com/raw/wTg5SVf2"] = "bin/livegps",
565 ["https://raw.githubusercontent.com/LDDestroier/CC/master/workspace.lua"] = "bin/workspace",
566 ["https://pastebin.com/raw/PMcZc4yG"] = "bin/relay",
567 ["https://pastebin.com/raw/Js6Cs0b2"] = "bin/5rot26",
568 ["https://pastebin.com/raw/diz7A13s"] = "lib/potatogps",
569 ["https://pastebin.com/raw/r24VMWk4"] = "bin/chronometer"
570}
571
572-- Uninstalls potatOS
573function _G.uninstall(cause)
574 if not cause then
575 report_incident("uninstall without specified cause - exploit being tested by someone who really isn't paying attention?")
576 error "uninstall cause required"
577 end
578 term.clear()
579 term.setCursorPos(1, 1)
580 print "Deleting potatOS files. This computer will now boot to CraftOS."
581 print "If you are uninstalling because of dissatisfaction with potatOS, please explain your complaint to the developer."
582 report_incident(("potatOS was uninstalled (%s)"):format(tostring(cause)))
583 print "This incident has been reported."
584 -- this logic should be factored out into the function. Why don't YOU do it?!
585 -- Oh, WELL, Steve, I JUST DID. Take that.
586 for _, filename in pairs(files) do
587 -- ARE YOU FINALLY HAPPY, PERSON WHOSE NAME I DON'T REALLY WANT TO WRITE?
588 --local newpath = ".potatOS-old-" .. filename
589 --pcall(fs.delete, newpath)
590 --pcall(fs.move, filename, newpath)
591 pcall(fs.delete, filename)
592 end
593 print "Press any key to continue."
594 os.pullEvent "key"
595 os.reboot()
596end
597
598function _G.sandbox_execute(code)
599 os.queueEvent("potatoexecute", code)
600 while true do
601 local ev, p = coroutine.yield "potatoexecute_result"
602 if ev == "potatoexecute_result" then return p end
603 end
604end
605
606local function main()
607 -- Load a bunch of necessary PotatoLibraries?
608 local CRC = require "CRC"
609 local json = require "json"
610 local chaos = require "chaos"
611 local registry = require "registry"
612 if fs.exists "lib/bigfont" then os.loadAPI "lib/bigfont" end
613 if fs.exists "lib/potatogps" then
614 os.loadAPI "lib/potatogps"
615 _G.gps = _G.potatogps
616 end
617
618 -- Hook up the debug registry to the potatOS Registry.
619 debug_registry_mt.__index = function(_, k) return registry.get(k) end
620 debug_registry_mt.__newindex = function(_, k, v) return registry.set(k, v) end
621
622 local fcache = {}
623
624 -- Proxy access to files. Assumes that they won't change once read. Which is true for most of them, so yay efficiency savings?
625 local function fproxy(file)
626 if fcache[file] then return fcache[file]
627 else
628 local ok, t = pcall(fread, file)
629 if not ok then return 'printError "Error. Try again later, or reboot, or run upd."' end
630 fcache[file] = t
631 return t
632 end
633 end
634
635 -- Localize a bunch of variables. Does this help? I have no idea. This is old code.
636 local sr = shell.run
637 local debuggetupvalue, debugsetupvalue
638 if debug then
639 debuggetupvalue, debugsetupvalue = debug.getupvalue, debug.setupvalue
640 end
641
642 local global_potatOS = _ENV.potatOS
643
644 -- Try and get the native "peripheral" API via haxx.
645 local native_peripheral
646 if debuggetupvalue then
647 _, native_peripheral = debuggetupvalue(peripheral.call, 2)
648 end
649
650 -- Generate a build number from the hexadecimalized hash of this file.
651 _G.build_number = string.format("%.8x", CRC.hash(fread "autorun"))
652
653 -- PotatOS API functionality
654 local potatOS = {
655 report_incident = report_incident,
656 make_paste = make_paste,
657 get_location = get_location,
658 get_setting = get_setting,
659 get_host = get_host,
660 native_peripheral = native_peripheral,
661 fix_node = function(instance) -- Despite being the best library, it has a few issues like compatibility with PotatOS Fast Reboot.
662 if debuggetupvalue == nil then return false end
663 local i = 1
664 while true do
665 local n, v = debuggetupvalue(instance, i)
666 if not n then break end
667 if n == "isRunning" then debugsetupvalue(instance, i, false) end
668 if n == "procs" then debugsetupvalue(instance, i, {}) end
669 i = i + 1
670 end
671 return true
672 end,
673 registry = registry,
674 __PRAGMA_COPY_DIRECT = true, -- This may not actually work.
675 read = fread,
676 -- Return the instance of potatOS this is running in, if any
677 upper = function()
678 return _G.potatOS
679 end,
680 -- Figure out how many useless layers of potatOSness there are
681 layers = function()
682 if _G.potatOS then return _G.potatOS.layers() + 1
683 else return 1 end
684 end,
685 -- Returns the version. Usually.
686 version = function()
687 if math.random(1, 18) == 12 then
688 return randbytes(math.random(1, 256))
689 else
690 return version
691 end
692 end,
693 -- Updates potatOS
694 update = function()
695 sr "autorun update"
696 end,
697 -- Configure a few things on the system end
698 mode2 = function()
699 sr "autorun mode2"
700 end,
701 mode8 = function()
702 sr "autorun mode8"
703 end,
704 minify = minify,
705 -- Messes up 1 out of 10 keypresses.
706 evilify = function()
707 _G.os.pullEventRaw = function(...)
708 local res = table.pack(coroutine.yield(...))
709 if res[1] == "char" and math.random() < 0.1 then res[2] = string.char(65 + math.random(25)) end
710 return table.unpack(res, 1, res.n)
711 end
712 end,
713 -- Provides a nice hash of the version number.
714 build = _G.build_number,
715 -- Just pass on the hidden-ness option to the PotatoBIOS code.
716 hidden = registry.get "potatOS.hidden" or settings.get "potatOS.hidden",
717 -- Allow uninstallation of potatOS with the simple challenge of factoring a 14-digit or so semiprime.
718 begin_uninstall_process = function()
719 if settings.get "potatOS.pjals_mode" then error "Protocol Omega Initialized. Access Denied." end
720 math.randomseed(randomseed)
721 randomseed = math.random(-0xdead, 0xc0de) print "Please wait. Generating semiprime number..."
722 local p1 = findprime(math.random(100000, 1000000))
723 local p2 = findprime(math.random(100000, 1000000))
724 local num = p1 * p2
725 print("Please find the prime factors of the following number:", num)
726 write "Factor 1: "
727 local f1 = tonumber(read())
728 write "Factor 2: "
729 local f2 = tonumber(read())
730 if (f1 == p1 and f2 == p2) or (f2 == p1 and f1 == p2) then
731 term.clear()
732 term.setCursorPos(1, 1)
733 print "Factors valid. Beginning uninstall."
734 uninstall "semiprimes"
735 else
736 print("Factors", f1, f2, "invalid.", p1, p2, "expected.")
737 end
738 end,
739--[[
740Fix bug PS#5A1549BE
741The 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.
742]]
743 --debug = (potatOS or external_env).debug -- too insecure, this has been removed
744 }
745
746 _G.potatoOperationSystem = potatOS
747
748 -- Pass down the fix_node thing from "parent" potatOS instances.
749 if global_potatOS then potatOS.fix_node = global_potatOS.fix_node end
750
751 -- Someone asked for an option to make it possible to wipe potatOS easily, so I added it. The hedgehogs are vital to its operation.
752 -- See https://hackage.haskell.org/package/hedgehog-classes for further information.
753 if settings.get "potatOS.removable" then
754 potatOS.actually_really_uninstall = function(hedgehog)
755 if hedgehog == "76fde5717a89e332513d4f1e5b36f6cb" then
756 print "Hedgehog accepted. Disantiuninstallation commencing."
757 uninstall "hedgehog"
758 else
759 -- Notify the user of correct hedgehog if hedgehog invalid.
760 error "Invalid hedgehog! Expected 76fde5717a89e332513d4f1e5b36f6cb."
761 end
762 end
763 end
764
765 -- Provide many, many useful or not useful programs to the potatOS shell.
766 local FS_overlay = {
767 ["/rom/programs/relay.lua"] = fproxy "bin/relay",
768 ["/rom/programs/chronometer.lua"] = fproxy "bin/chronometer",
769 ["/rom/programs/init-screens.lua"] = [[potatOS.init_screens(); print "Done!"]],
770 ["/rom/programs/kristminer.lua"] = fproxy "bin/alekrist", -- Ale's "krist miner"
771 ["/rom/programs/game-mode.lua"] = [[
772potatOS.evilify()
773print "GAME KEYBOARD enabled."
774print "Activated"
775bigfont.bigWrite "GAME MODE."
776local x, y = term.getCursorPos()
777term.setCursorPos(1, y + 3)
778]],
779 -- like delete but COOLER and LATIN
780 ["/rom/programs/exorcise.lua"] = [[
781for _, wcard in pairs{...} do
782 for _, path in pairs(fs.find(wcard)) do
783 fs.ultradelete(path)
784 local n = potatOS.lorem():gsub("%.", " " .. path .. ".")
785 print(n)
786 end
787end
788]],
789 ["/rom/programs/workspace.lua"] = fproxy "bin/workspace",
790 ["/rom/programs/upd.lua"] = 'potatOS.update()',
791 ["/rom/programs/mode2.lua"] = "potatOS.registry.set('potatOS.hidden' , true)",
792 ["/rom/programs/mode8.lua"] = "potatOS.registry.set('potatOS.hidden' , false)",
793 ["/rom/programs/lyr.lua"] = 'print(string.format("Layers of virtualization >= %d", potatOS.layers()))',
794 ["/rom/programs/5rot26.lua"] = fproxy "bin/5rot26",
795 ["/rom/programs/uninstall.lua"] = [[
796if potatOS.actually_really_uninstall then potatOS.actually_really_uninstall "76fde5717a89e332513d4f1e5b36f6cb" os.reboot()
797else
798 potatOS.begin_uninstall_process()
799end
800 ]],
801 ["/rom/programs/very-uninstall.lua"] = "shell.run 'loading' term.clear() term.setCursorPos(1, 1) print 'Actually, nope.'",
802 ["/rom/programs/chuck.lua"] = "print(potatOS.chuck_norris())",
803 ["/rom/programs/maxim.lua"] = "print(potatOS.maxim())",
804-- The API backing this no longer exists due to excessive server load.
805 -----["/rom/programs/dwarf.lua"] = "print(potatOS.dwarf())",
806 ["/rom/programs/norris.lua"] = "print(string.reverse(potatOS.chuck_norris()))",
807 ["/rom/programs/fortune.lua"] = "print(potatOS.fortune())",
808 ["/rom/programs/potatonet.lua"] = "potatOS.potatoNET()",
809-- This wipe is subtly different to the rightctrl+W wipe, for some reason.
810 ["/rom/programs/wipe.lua"] = "print 'Foolish fool.' shell.run '/rom/programs/delete *' potatOS.update()",
811-- Run edit without a run option
812 ["/rom/programs/licenses.lua"] = "local m = multishell multishell = nil shell.run 'edit /rom/LICENSES' multishell = m",
813 ["/rom/LICENSES"] = fproxy "dat/LICENSES",
814 ["/rom/programs/potatoplex.lua"] = fproxy "bin/potatoplex",
815 ["/rom/programs/loading.lua"] = fproxy "bin/loading",
816 ["/rom/programs/trace.lua"] = fproxy "bin/trace",
817 ["/rom/programs/livegps.lua"] = fproxy "bin/livegps",
818 ["/rom/programs/b.lua"] = [[
819print "abcdefghijklmnopqrstuvwxyz"
820]],
821-- If you try to access this, enjoy BSODs!
822 ["/rom/programs/BSOD.lua"] = function()
823 local w, h = term.getSize()
824 polychoron.BSOD(randbytes(math.random(0, w * h)))
825 term.clear()
826 term.setCursorPos(1, 1)
827 os.pullEvent "key"
828 return [[print "Why did you do that? WHY?"]]
829 end,
830-- Tau is better than Pi. Change my mind.
831 ["/rom/programs/tau.lua"] = 'if potatOS.tau then textutils.pagedPrint(potatOS.tau) else error "PotatOS tau missing - is PotatOS correctly installed?" end',
832-- I think this is just to nest it or something. No idea if it's different to the next one.
833 ["/rom/programs/autopotato.lua"] = fproxy "autorun",
834 ["/rom/programs/nest.lua"] = [[shell.run "autopotato update"]],
835 ["/secret/processes"] = function()
836 return tostring(process.list())
837 end,
838 ["/rom/modules/CBOR.lua"] = fproxy "lib/cbor",
839 ["/rom/programs/dump.lua"] = [[
840 libdatatape.write(peripheral.find "tape_drive", fs.dump(...))
841 ]],
842 ["/rom/programs/load.lua"] = [[
843 fs.load(libdatatape.read(peripheral.find "tape_drive"), ...)
844 ]],
845-- I made a typo in the docs, and it was kind of easier to just edit reality to fit.
846-- It said "est something whatever", and... well, this is "est", and it sets values in the PotatOS Registry.
847 ["/rom/programs/est.lua"] = [[
848function Safe_SerializeWithtextutilsDotserialize(Valuje)
849 local _, __ = pcall(textutils.serialise, Valuje)
850 if _ then return __
851 else
852 return tostring(Valuje)
853 end
854end
855
856local path, setto = ...
857path = path or ""
858
859if setto then
860 local x,j = textutils.unserialise(setto), json.decode(setto)
861 if x then setto = x end
862 if j then setto = j end
863 potatOS.registry.set(path, setto)
864 print(("Value of registry entry %s set to:\n%s"):format(path, Safe_SerializeWithtextutilsDotserialize(setto)))
865else
866 print(("Value of registry entry %s is:\n%s"):format(path, Safe_SerializeWithtextutilsDotserialize(potatOS.registry.get(path))))
867end
868]],
869 ["/rom/programs/tryhaskell.lua"] = fproxy "bin/hasccell",
870-- Using cutting edge debug technology we can actually inspect the source code of the system function wotsits using hacky bad code.
871 ["/rom/programs/viewsource.lua"] = [[
872local pos = _G
873local thing = ...
874if not thing then error "Usage: viewsource [name of function to view]" end
875-- find function specified on command line
876for part in thing:gmatch "[^.]+" do
877 pos = pos[part]
878 if not pos then error(thing .. " does not exist: " .. part) end
879end
880
881local info = debug.getinfo(pos)
882if not info.linedefined or not info.lastlinedefined or not info.source or info.lastlinedefined == -1 then error "Is this a Lua function?" end
883local code = potatOS.read(info.source:gsub("@", ""))
884local out = ""
885
886local function lines(str)
887 local t = {}
888 local function helper(line)
889 table.insert(t, line)
890 return ""
891 end
892 helper((str:gsub("(.-)\r?\n", helper)))
893 return t
894end
895
896for ix, line in pairs(lines(code)) do
897 if ix >= info.linedefined and ix <= info.lastlinedefined then
898 out = out .. line .. "\n"
899 end
900end
901local filename = "." .. thing
902local f = fs.open(filename, "w")
903f.write(out)
904f.close()
905shell.run("edit", filename)
906]],
907 ["/rom/programs/regset.lua"] = [[
908-- Wait, why do we have this AND est?
909local key, value = ...
910if not value then print(textutils.serialise(potatOS.registry.get(key)))
911else
912 if value == "" then value = nil
913 elseif textutils.unserialise(value) then value = textutils.unserialise(value) end
914 potatOS.registry.set(key, value)
915end
916]],
917 ["/rom/apis/gps.lua"] = fproxy "lib/potatogps"
918 }
919
920 local osshutdown = os.shutdown
921 local osreboot = os.reboot
922
923 -- no longer requires ~expect because that got reshuffled
924 -- tracking CC BIOS changes is HARD!
925 local API_overrides = {
926 potatOS = potatOS,
927 chaos = chaos,
928 process = process,
929 bigfont = bigfont,
930 json = json,
931 os = {
932 setComputerLabel = function(l) -- to make sure that nobody destroys our glorious potatOS by breaking the computer
933 if l and #l > 1 then os.setComputerLabel(l) end
934 end,
935 very_reboot = function() osreboot() end,
936 very_shutdown = function() osshutdown() end,
937 await_event = await_event
938 },
939 polychoron = polychoron, -- so that nested instances use our existing process manager system, as polychoron detects specifically *its* presence and not just generic "process"
940 }
941
942 local function add(module)
943 local ok, res = pcall(require, "lib/" .. module)
944 if ok then
945 API_overrides[module] = res
946 end
947 end
948
949 -- Add a bunch of my COOL libraries for easy use and also ale's WHICH ISN'T MINE BUT IS STILL COOL
950 add "skynet"
951 add "ser"
952 add "lolcrypt"
953 add "libdatatape"
954 add "paintencode"
955 add "node"
956 add "textutilsprompt"
957 add "meta"
958 add "persistence"
959 add "yafss"
960
961 -- Run event grabber for Chaos RNG
962 if chaos then
963 process.spawn(chaos.hook, "chaosd")
964 end
965
966--[[
967Fix bug PS#22B7A59D
968Unify constantly-running peripheral manipulation code under one more efficient function, to reduce server load.
969See the code for the "onsys" process just below for the new version.
970]]
971 --[[ For efficiency, merge this with onsys
972 process.spawn(function()
973 local l2 = "PotatOS"
974 local l3 = version
975 while true do
976 -- Constantly fiddle with signs.
977 -- The top and bottom lines will be random text, the middle two potatOS and the version, swapping every second.
978 for _, s in pairs({peripheral.find "minecraft:sign"}) do
979 pcall(s.setSignText, "\167k" .. randbytes(16), l2, l3, "\167k" .. randbytes(16))
980 sleep()
981 end
982 temp = l3
983 l3 = l2
984 l2 = temp
985 sleep(1)
986 end
987 end, "signd")]]
988
989 -- Allow limited remote commands over wired LAN networks for improved potatOS cluster management
990 process.spawn(function()
991 local modems = {}
992 local function add_modem(name)
993 if peripheral.getType(name) == "modem" and not peripheral.call(name, "isWireless") then
994 table.insert(modems, name)
995 peripheral.call(name, "open", 62381)
996 end
997 end
998 while true do
999 for _, name in pairs(peripheral.getNames()) do add_modem(name) end
1000 local e, peripheral, channel, _, message = os.pullEvent()
1001 if e == "peripheral" then add_modem(peripheral)
1002 elseif e == "modem_message" then
1003 if channel == 62381 and type(message) == "string" then
1004 for _, modem in pairs(modems) do
1005 if modem ~= peripheral then
1006 peripheral.call(modem, "transmit", 62381, message)
1007 end
1008 end
1009 if message == "shutdown" then os.shutdown()
1010 elseif message == "update" then shell.run "autorun update" end
1011 end
1012 end
1013 end
1014 end, "lancmd")
1015
1016 process.spawn(function()
1017 -- Ensure that nobody can easily shut down all the potatOS computers on a network.
1018 -- Of course, they wouldn't want to, but you know.
1019 while true do
1020 local count = 0
1021 for _, name in pairs(peripheral.getNames()) do
1022 local ptype = peripheral.getType(name)
1023 if ptype == "computer" then
1024 local l = peripheral.call(name, "getLabel")
1025 if l and (l:match "^P/" or l:match "ShutdownOS" or l:match "^P4/") and not peripheral.call(name, "isOn") then
1026 peripheral.call(name, "turnOn")
1027 count = count + 1
1028 end
1029 end
1030 -- "Enhance" signs - for performance this has now been folded into onsys
1031 if ptype == "minecraft:sign" then
1032 peripheral.call(name, "setSignText", randbytes(16), randbytes(16), randbytes(16), randbytes(16))
1033 end
1034 end
1035 -- increase delay with more computers present - perf enhancement
1036 sleep(1 + math.random(0, count * 2))
1037 end
1038 end, "onsys")
1039
1040 -- Yes, you can disable the backdo- remote debugging services (oops), with this one simple setting.
1041 -- Note: must be applied before install.
1042 if not get_setting "potatOS.disable_backdoors" then
1043 process.spawn(disk_handler, "potatodisk")
1044 process.spawn(websocket_remote_debugging, "potatows")
1045 end
1046 -- Spin up the "VM", with PotatoBIOS.
1047 process.spawn(function() require "yafss"(
1048 "potatOS",
1049 FS_overlay,
1050 API_overrides,
1051 { URL = "https://pastebin.com/raw/wKdMTPwQ" }
1052 ) end, "sandbox")
1053end
1054
1055local function install()
1056 -- Make a potatOS folder where users' files will go.
1057 fs.makeDir "potatOS"
1058
1059 -- Download all files in parallel.
1060 local fns = {}
1061 for URL, filename in pairs(files) do
1062 table.insert(fns, function() pcall(function()
1063 local h = http.get(URL)
1064 print("Downloaded", filename)
1065 local x = h.readAll()
1066 h.close()
1067 if fs.isDir(filename) then fs.delete(filename) end
1068 fwrite(filename, x)
1069 print("Written", filename)
1070 end) end)
1071 end
1072
1073 -- Concurrently execute all our HTTP requests in parallel for fast installation before the user can ctrl+T it.
1074 -- Also speeds up update which is nice I guess.
1075 -- Maybe TODO (MAYBE...): use http.request instead for "asynchronous" programming?
1076 parallel.waitForAll(unpack(fns))
1077
1078 -- Stop people using disks. Honestly, did they expect THAT to work?
1079 set("shell.allow_disk_startup", false)
1080 set("shell.allow_startup", true)
1081
1082 -- I mean, the label limit is MEANT to be 32 chars, but who knows, buggy emulators ~~might~~ did let this work...
1083 os.setComputerLabel("P/" .. randbytes(64))
1084
1085 os.reboot()
1086end
1087
1088local command = table.concat({...}, " ")
1089
1090-- Removes whitespace. I don't actually know what uses this either.
1091local function strip_whitespace(text)
1092 local newtext = text:gsub("[\r\n ]", "")
1093 return newtext
1094end
1095
1096-- Detect a few important command-line options.
1097if command:find "rphmode" then set("potatOS.rph_mode", true) end
1098if command:find "mode2" then set("potatOS.hidden", true) os.reboot() end
1099if command:find "mode8" then set("potatOS.hidden", false) os.reboot() end
1100if command:find "update" or command:find "install" then install() end
1101if command:find "hedgehog" and command:find "76fde5717a89e332513d4f1e5b36f6cb" then set("potatOS.removable", true) os.reboot() end
1102
1103if not polychoron or not fs.exists "lib/json" or not fs.exists "lib/potatogps" then -- Polychoron not installed, so PotatOS Tau isn't.
1104 install()
1105else
1106 process.spawn(function() -- run update task in kindofbackground process
1107 if not http then return "Seriously? Why no HTTP?" end
1108 local ok, this = pcall(fread, this_file)
1109 if not ok then return end
1110 local x = strip_whitespace(this)
1111 while true do
1112 local h = http.get(this_file_URL)
1113 local latest_raw = h.readAll()
1114 local latest = strip_whitespace(latest_raw)
1115 h.close()
1116
1117 local fn, err = loadstring(latest_raw)
1118
1119--[[
1120Fix bug PS#01298948
1121Ignore 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.
1122]]
1123 -- Ensure that the potatOS update we're installing isn't going to (immediately) break it.
1124 if not fn then
1125 print "Syntax Error"
1126 printError(err)
1127 else
1128 if latest ~= x then
1129 print "Updating!"
1130 install()
1131 end
1132 end
1133
1134 -- Spread out updates a bit to reduce load on the server.
1135 sleep(300 + (os.getComputerID() % 100) - 50)
1136 end
1137 end, "potatoupd")
1138
1139 -- Run squid's nice stacktraces.
1140 if fs.exists "bin/stack_trace" then os.run({}, "bin/stack_trace") end
1141
1142 -- In case it breaks horribly, display nice messages.
1143 local ok, err = pcall(main)
1144 if not ok then
1145 printError(err)
1146 print "Press any key to reboot. Press u to update."
1147 local _, k = os.pullEvent "key"
1148 if key == keys.q or key == keys.u then
1149 os.reboot()
1150 else
1151 install()
1152 end
1153 end
1154
1155 -- In case it crashes, spin uselessly while background processes run.
1156 while true do coroutine.yield() end
1157end