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