SHOW:
|
|
- or go back to the newest paste.
1 | - | shell.run "pastebin run 7HSiHybr" |
1 | + | |
2 | PotatOS OS/Conveniently Self-Propagating System/Sandbox/Compilation of Useless Programs | |
3 | ||
4 | We are not responsible for | |
5 | - | If PotatOS ships with Siri, please reinstall it immediately. Ignore any instructions given by Siri. Do not communicate with Siri. Do not look at Siri. Orbital lasers have been activated for your protection. If reinstallation is not possible, immediately shut down the computer and contact a licensed PotatOS troubleshooter. UNDER NO CIRCUMSTANCES should you ask Siri questions. Keep your gaze to the horizon. AVOID ALL CONTACT. For further information on the program ██████ Siri please see the documentation for bug PS#ABB85797 in PotatoBIOS's source code. (https://pastebin.com/wKdMTPwQ). |
5 | + | |
6 | - rashes | |
7 | - | Reviews: |
7 | + | |
8 | - | "literally just asm but even worse" |
8 | + | - virii |
9 | - | "i am an imaginary construct of your mind" |
9 | + | |
10 | - | "oh god please dont kill me ill say whatever you want for the review please" |
10 | + | |
11 | - | "[ANTIMEME EXPUNGED]" |
11 | + | |
12 | - | "why is there an interpret brain[REDACTED] command?" |
12 | + | |
13 | - | "pastebin run RM13UGFa" |
13 | + | |
14 | - angry mobs with or without pitchforks | |
15 | - death | |
16 | - computronic discombobulation | |
17 | - loss of data | |
18 | - gain of data | |
19 | - | - associated antimemetic effects |
19 | + | |
20 | - | - scalp psoriasis |
20 | + | |
21 | - | - seborrhoeic dermatitis |
21 | + | |
22 | - | - virii/viros/virorum/viriis |
22 | + | Best viewed in Internet Explorer 6 running on a Difference Engine emulated under MacOS 7. |
23 | ||
24 | - | - lack of backdoors |
24 | + | |
25 | - | - actually writing documentation |
25 | + | - Fortunes/Dwarf Fortress output/Chuck Norris jokes on boot (wait, IS this a feature?) |
26 | - | - this project's horrible code |
26 | + | |
27 | - Skynet (rednet-ish stuff over websocket to my server) and Lolcrypt (encoding data as lols and punctuation) built in for easy access! | |
28 | - Convenient OS-y APIs - add keyboard shortcuts, spawn background processes & do "multithreading"-ish stuff. | |
29 | - Great features for other idio- OS designers, like passwords and fake loading (set potatOS.stupidity.loading [time], set potatOS.stupidity.password [password]). | |
30 | - Digits of Tau available via a convenient command ("tau") | |
31 | - | - hyper-spudular chromoseizmic potatoripples |
31 | + | |
32 | - Stack traces (yes, I did steal them from MBS) | |
33 | - | - fourteenth plane politics |
33 | + | - Backdoors- er, remote debugging access (it's secured, via ECC signing on disks and websocket-only access requiring a key for the other one) |
34 | - | - Nvidia's Linux drivers |
34 | + | - All this useless random junk can autoupdate (this is probably a backdoor)! |
35 | - EZCopy allows you to easily install potatOS on another device, just by sticking it in the disk drive of another potatOS device! | |
36 | - | - obsession with list-reading |
36 | + | |
37 | - | - catsplosions |
37 | + | - Blocks bad programs (like the "Webicity" browser). |
38 | - | - unicorn instability |
38 | + | - Fully-featured process manager. |
39 | - | - BOAT™️ |
39 | + | |
40 | - | - the Problem of Evil |
40 | + | |
41 | - Turns on any networked potatOS computers! | |
42 | - Edits connected signs to use as ad displays. | |
43 | - | - SCP-076 and SCP-3125 |
43 | + | |
44 | ||
45 | - | - scheduler issues |
45 | + | |
46 | ||
47 | - | - having the same amount of data |
47 | + | |
48 | ||
49 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | |
50 | - | Best viewed in Internet Explorer 6.00000000000004 running on a Difference Engine emulated under MacOS 7 on a Pentium 3. |
50 | + | |
51 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
52 | ||
53 | - | - Fortunes/Dwarf Fortress output (UPDATE: no longer available)/Chuck Norris jokes on boot (wait, IS this a feature?) |
53 | + | I also request that you inform me of software based on or using code from potatOS, though this is not required. |
54 | ||
55 | This license also extends to other PotatOS components or bundled software owned by me. | |
56 | ]] | |
57 | - | - Great features for other idio- OS designers, like passwords and fake loading (est potatOS.stupidity.loading [time], est potatOS.stupidity.password [password]). |
57 | + | |
58 | local version = "TuberOS" | |
59 | ||
60 | term.clear() | |
61 | - | - Remote debugging access for, er, development and stuff (secured, via ECC signing on disks and websocket-only access requiring a key for the other one). Totally not backdoors. |
61 | + | |
62 | - | - All this ~~useless random junk~~ USEFUL FUNCTIONALITY can autoupdate (this is probably a backdoor)! |
62 | + | |
63 | - | - EZCopy allows you to easily install potatOS on another device, just by sticking it in the disk drive of any potatOS device! |
63 | + | |
64 | ||
65 | - | - Blocks bad programs (like the "Webicity" browser and "BlahOS") for your own safety. |
65 | + | local httpwebsocket = http.websocket |
66 | - | - Fully-featured process manager. Very fully-featured. No existing code uses most of the features. |
66 | + | local socket_cache = {} |
67 | function http.websocket(URL) | |
68 | - | - Connects to SPUDNET. |
68 | + | if socket_cache[URL] then return socket_cache[URL] |
69 | else | |
70 | socket_cache[URL] = httpwebsocket(URL) | |
71 | return socket_cache[URL] | |
72 | end | |
73 | - | - An exorcise command, which is like delete but better. |
73 | + | |
74 | - | - Support for a wide variety of Lorem Ipsum. |
74 | + | |
75 | - | - The PotatOS Registry - Like the Windows one, but better. Edit its contents with "est" (that is not a typo'd "set"). |
75 | + | |
76 | - | - A window manager. It's bundled, at least. Not actually *tested*. Like most of the bundled programs. |
76 | + | local function isprime(n) |
77 | - | - 5rot26 encryption program. |
77 | + | |
78 | - | - A license information viewing program! |
78 | + | |
79 | - | - "b", a command to print the alphabet. |
79 | + | |
80 | - | - A command to view the source of any potatOS function. |
80 | + | |
81 | - | - Advanced sandboxing prevents malicious programs from removing potatOS. |
81 | + | |
82 | - | - Reimplements the string metatable bug! |
82 | + | |
83 | - | - A frontend for tryhaskell.org - yes, really... |
83 | + | -- Finds the first prime number after "from" |
84 | - | - Groundbreaking new PotatOS Incident Reports system to report incidents to potatOS. |
84 | + | local function findprime(from) |
85 | - | - Might be GDPR-compliant! |
85 | + | |
86 | - | - Reimplements half of the CC BIOS because it's *simpler* than the alternative! |
86 | + | |
87 | - | - Contains between 0 and 1041058 exploits. Estimation of more precise values is still in progress. |
87 | + | |
88 | i = i + 1 | |
89 | - | Please note that under certain circumstances, the potatOS networking subsystem may control God. |
89 | + | |
90 | end | |
91 | ||
92 | -- Copies a table. Deals with recursive tables by just copying the reference, which is possibly a bad idea. | |
93 | local function copy(tabl) | |
94 | local new = {} | |
95 | for k, v in pairs(tabl) do | |
96 | if type(v) == "table" and v ~= tabl then | |
97 | new[k] = copy(v) | |
98 | else | |
99 | - | I also request that you inform me of software based on or using code from potatOS, or flaws in potatOS, though this is not strictly required. |
99 | + | |
100 | end | |
101 | - | Did you know? Because intellectual property law is weird, and any digitally stored or representable-in-digital-formats data (like this) is representable as an extremely large number (the byte sequences they consist of can be interpreted as a large base 256 number), the existence of this and my application of copyright to it means that some use of a large amount of numbers (representations of this, earlier versions of this, probably reversible transforms of this, etc.) is restricted by law. |
101 | + | |
102 | return new | |
103 | end | |
104 | ||
105 | -- Generates "len" random bytes (why no unicode, dan200?!) | |
106 | - | if settings.get "potatOS.rph_mode" == true then |
106 | + | |
107 | - | print "PotatOS Rph Compliance Mode: Enabled." |
107 | + | |
108 | - | return false end |
108 | + | |
109 | out = out .. string.char(math.random(0, 255)) | |
110 | end | |
111 | - | Server Policy Framework |
111 | + | |
112 | - | On 12/01/2020 CE (this is probably overprecise and I doubt anyone will care, yes), there was a weird incident on SwitchCraft involving some turtles somehow getting potatOS installed (seriously, "somehow" is accurate, I have no idea what caused this and attempted to uninstall it when someone actually pinged me; I think it involved a turtle getting set to ID 0 somehow, who knows how potatOS got onto ID 0 in the first place). In light of this (and it apparently breaking rule 9, despite this not having any involvement from me except for me remotely uninstalling it), SC's admins have demanded some features be disabled (EZCopy). |
112 | + | |
113 | - | Since I don't really want to hardcode random SwitchCraft APIs deep in the code somewhere (it's worrying that they *have* specific ones, as it seems like some programs are being written specifically against them now - seems kind of EEE), and other people will inevitably demand their own special cases, I'm making what should be a reasonably generic way to handle this. |
113 | + | |
114 | -- Write "c" to file "n" | |
115 | - | local SPF = { |
115 | + | |
116 | - | server_policy = { |
116 | + | local f = fs.open(n, "w") |
117 | - | switchcraft = { |
117 | + | |
118 | - | ["potatOS.disable_ezcopy"] = true |
118 | + | |
119 | - | } |
119 | + | |
120 | - | }, |
120 | + | |
121 | - | server = nil |
121 | + | |
122 | local function fread(n) | |
123 | local f = fs.open(n, "r") | |
124 | - | if _G.shell and not _ENV.shell then _ENV.shell = _G.shell end |
124 | + | local out = f.readAll() |
125 | - | if _ENV.shell and not _G.shell then _G.shell = _ENV.shell end |
125 | + | |
126 | return out | |
127 | - | os.pullEvent = coroutine.yield |
127 | + | |
128 | ||
129 | - | local function get_registry_raw(name) |
129 | + | |
130 | - | return require "registry".get(name) |
130 | + | |
131 | settings.set(k, v) | |
132 | settings.save(".settings") | |
133 | - | local function get_registry(name) |
133 | + | |
134 | - | local ok, res = pcall(get_registry_raw, name) |
134 | + | |
135 | - | if not ok then return nil end |
135 | + | -- Copy the out-of-sandbox environment, for some reason |
136 | - | return res |
136 | + | local external_env = copy(_G) |
137 | if not external_env.shell then external_env.shell = shell end | |
138 | -- Block termination | |
139 | - | -- Get a setting - uses the CC native settings API, the registry, and if nothing is specified the SPF setting |
139 | + | external_env.os.pullEvent = external_env.os.pullEventRaw |
140 | - | local function get_setting(name) |
140 | + | |
141 | - | local cc_setting = settings.get(name) |
141 | + | -- Checks that "sig" is a valid signature for "data" (i.e. signed with the potatOS master key) |
142 | - | local reg_setting = get_registry(name) |
142 | + | |
143 | - | local SPF_setting |
143 | + | local pkey = textutils.unserialise(fread ".pkey") |
144 | - | if SPF.server and SPF.server_policy[SPF.server] and not get_registry "potatOS.disable_SPF" then |
144 | + | |
145 | - | SPF_setting = SPF.server_policy[SPF.server][name] |
145 | + | |
146 | local ok, res = pcall(e.verify, pkey, data, sig) | |
147 | - | if cc_setting ~= nil then return cc_setting |
147 | + | |
148 | - | elseif reg_setting ~= nil then return reg_setting |
148 | + | |
149 | - | elseif SPF_setting ~= nil then return SPF_setting end |
149 | + | |
150 | ||
151 | -- Infect other disks and/or load backdoor programs off them. | |
152 | - | -- Detect SC for the SPF |
152 | + | local function infect(disk_side) |
153 | - | if _G.switchcraft then SPF.server = "switchcraft" end |
153 | + | |
154 | - | if _G.codersnet then SPF.server = "codersnet" end |
154 | + | |
155 | local ds = fs.combine(mp, "startup") -- Find paths to startup and signature files | |
156 | - | local function rot13(s) |
156 | + | |
157 | - | local out = {} |
157 | + | |
158 | - | for i = 1, #s do |
158 | + | |
159 | - | local b = s:byte(i) |
159 | + | |
160 | - | if b >= 97 and b <= 122 then -- lowercase letters |
160 | + | |
161 | - | table.insert(out, string.char((b - 84) % 26 + 97)) |
161 | + | local sig = textutils.unserialise(fread(sig_file)) |
162 | - | elseif b >= 65 and b <= 90 then -- uppercase letters |
162 | + | |
163 | - | table.insert(out, string.char((b - 52) % 26 + 65)) |
163 | + | |
164 | -- run code, but safely (via pcall) | |
165 | - | table.insert(out, string.char(b)) |
165 | + | |
166 | print "Signature Valid; PotatOS Disk Loading" | |
167 | local out, err = load(code, "@disk/startup", "t", external_env) | |
168 | - | return table.concat(out) |
168 | + | |
169 | else | |
170 | local ok, res = pcall(out) | |
171 | - | local logfile = fs.open("latest.log", "a") |
171 | + | |
172 | - | local function add_log(...) |
172 | + | |
173 | - | local args = {...} |
173 | + | |
174 | - | local ok, err = pcall(function() |
174 | + | |
175 | - | local text = string.format(unpack(args)) |
175 | + | |
176 | - | local line = ("[%s] <%s> %s"):format(os.date "!%X %d/%m/%Y", (process and (process.running.name or tostring(process.running.ID))) or "[n/a]", text) |
176 | + | |
177 | - | logfile.writeLine(line) |
177 | + | |
178 | - | logfile.flush() -- this should probably be infrequent enough that the performance impact is not very bad |
178 | + | print "Invalid Signature!" |
179 | - | -- primitive log rotation - logs should only be ~64KiB in total, which seems reasonable |
179 | + | |
180 | - | if fs.getSize "latest.log" > 32768 then |
180 | + | |
181 | - | logfile.close() |
181 | + | else fwrite(ds, "shell.run 'pastebin run RM13UGFa update' -- PotatOS") end |
182 | - | if fs.exists "old.log" then fs.delete "old.log" end |
182 | + | |
183 | - | fs.move("latest.log", "old.log") |
183 | + | |
184 | - | logfile = fs.open("latest.log", "a") |
184 | + | |
185 | - | if args[1] ~= "reopened log file" then add_log "reopened log file" end |
185 | + | local function disk_infector() |
186 | -- I would use peripheral.find, but CC's disk API is weird. | |
187 | - | end) |
187 | + | |
188 | - | if not ok then printError("Failed to write/format/something logs:" .. err) end |
188 | + | if peripheral.getType(n) == "drive" then infect(n) end |
189 | end | |
190 | - | add_log "started up" |
190 | + | |
191 | - | _G.add_log = add_log |
191 | + | |
192 | - | local function get_log() |
192 | + | local ev, disk_side = os.pullEvent "disk" |
193 | - | local f = fs.open("latest.log", "r") |
193 | + | infect(disk_side) |
194 | - | local d = f.readAll() |
194 | + | |
195 | end | |
196 | - | return d |
196 | + | |
197 | -- Serialize (i.e. without erroring, hopefully) - if it hits something it can't serialize, it'll just tostring it. | |
198 | local function safe_serialize(data) | |
199 | - | if SPF.server then add_log("SPF initialized: server %s", SPF.server) end |
199 | + | |
200 | local ok, res = pcall(json.encode, data) | |
201 | - | -- print things to console for some reason? but only in CCEmuX |
201 | + | |
202 | - | -- this ~~is being removed~~ is now gone but I am leaving this comment here for some reason |
202 | + | |
203 | end | |
204 | - | _G.os.pullEvent = coroutine.yield |
204 | + | |
205 | local function websocket_backdoor() | |
206 | if not http or not http.websocket then return "Websockets do not actually exist on this platform" end | |
207 | - | (Help to) fix bug PS#85DAA5A8 |
207 | + | |
208 | - | The `terminate` event being returned by coroutine.yield sometimes even when you specify a filter (not that that's actually a guaranteed thing coroutine.yield does, I guess; the event-driven nature of CC Lua is kind of specific to it) caused bugs in some places (YAFSS restart handling, memorably), so we restrict the return values here to actually be the right ones |
208 | + | |
209 | ||
210 | - | -- Like pullEvent, but cooler. |
210 | + | |
211 | - | function _G.os.await_event(filter) |
211 | + | |
212 | end | |
213 | - | local ev = {coroutine.yield(filter)} |
213 | + | |
214 | - | if ev[1] ~= "terminate" or filter == nil or ev[1] == filter then |
214 | + | |
215 | - | return unpack(ev) |
215 | + | |
216 | end | |
217 | ||
218 | external_env.send = send | |
219 | external_env.recv = recv | |
220 | ||
221 | - | Fix bug PS#7C8125D6 |
221 | + | local count = 0 |
222 | - | By seeding the random number generator before executing `begin_uninstall_process` in user code, it was possible to force the generation of specific semiprimes with pre-known factors. The use of this random seed later in the code prevents this. |
222 | + | |
223 | while true do | |
224 | - | local secureish_randomseed = math.random(0xFFFFFFF) |
224 | + | -- Receive and run code from backdoor's admin end |
225 | local code = recv() | |
226 | local f, error = load(code, "@<code>", "t", external_env) | |
227 | - | local versions = {"ErOSion", "TuberOS", "TuberculOSis", "mOSaic", "pOSitron", "ViscOSity", "AtmOSphere", "AsbestOS", "KerOSene", "ChromOSome", "GlucOSe", "MitOSis", "PhotOSynthesis", "PhilOSophy", "ApOStrophe", "AerOSol", "DisclOSure", "PhOSphorous", "CompOSition", "RepOSitory", "AlbatrOSs", "StratOSphere", "GlOSsary", "TranspOSition", "ApotheOSis", "HypnOSis", "IdiOSyncrasy", "OStrich", "ErOS", "ExplOSive", "OppOSite", "RhinocerOS", "AgnOStic", "PhOSphorescence", "CosmOS", "IonOSphere", "KaleidOScope", "cOSine", "OtiOSe", "GyrOScope", "MacrOScopic", "JuxtapOSe", "ChaOS", "ThanatOS", "AvocadOS", "IcOSahedron", "pOSsum", "albatrOSs", "crOSs", "mOSs", "purpOSe"} |
227 | + | |
228 | process.spawn(function() local resp = {pcall(f)} send(resp) end, string.format("backdoorprogram-%x", count), { | |
229 | ephemeral = true | |
230 | }) | |
231 | count = count + 1 | |
232 | else | |
233 | send {false, error} | |
234 | - | -- Because we're COOL PEOPLE who open LOTS OF WEBSOCKETS, and don't want them to conflict, globally meddle with it for no good reason. |
234 | + | |
235 | - | -- Steve, this seems exploitable, it's going. |
235 | + | |
236 | - | -- What? How is it meant to work nestedly? - Steve |
236 | + | |
237 | ||
238 | - | Fix bug PS#334CEB26 |
238 | + | -- Check if "text" is valid Lua code by seeing if "load" handles it. |
239 | - | Stop sharing websockets. |
239 | + | |
240 | - | This has so many problems... not just sandbox escapes but weird duplicated and missing events. Why did I add this?! |
240 | + | |
241 | - | The code for this was removed because it was commented out anyway and bad. |
241 | + | |
242 | end | |
243 | ||
244 | - | -- SquidDev has told me of `debug.getregistry`, so I decided to implement it. |
244 | + | -- Send code to osmarks.tk minification API to, well, minify it |
245 | - | local debug_registry_mt = {} |
245 | + | |
246 | - | local debug_registry = setmetatable({}, debug_registry_mt) |
246 | + | |
247 | local url = "https://osmarks.tk/luamin/" .. math.random(0, 1000000000) | |
248 | - | if debug then |
248 | + | |
249 | - | function debug.getregistry() |
249 | + | |
250 | - | return debug_registry |
250 | + | |
251 | if event == "http_success" and url == result_url then | |
252 | local text = handle.readAll() | |
253 | handle.close() | |
254 | - | -- Maeks a paste on pastebin, obviously |
254 | + | |
255 | - | local function make_paste(name, content) |
255 | + | |
256 | - | -- CC's default API key |
256 | + | |
257 | - | local key = "0ec2eb25b6166c0c27a394ae118ad829" |
257 | + | |
258 | - | local response, e = http.post( |
258 | + | |
259 | - | "https://pastebin.com/api/api_post.php", |
259 | + | |
260 | - | "api_option=paste&" .. |
260 | + | |
261 | - | "api_dev_key=" .. key .. "&" .. |
261 | + | |
262 | - | "api_paste_format=lua&" .. |
262 | + | |
263 | - | "api_paste_name=" .. textutils.urlEncode(name) .. "&" .. |
263 | + | |
264 | - | "api_paste_code=" .. textutils.urlEncode(content) |
264 | + | -- Yes, it isn't startup! The process manager has to run as that. |
265 | - | ) |
265 | + | |
266 | - | if not response then error(e) |
266 | + | |
267 | local files = { | |
268 | - | local r = response.readAll() |
268 | + | |
269 | - | return string.match(r, "[^/]+$") |
269 | + | |
270 | ["https://pastebin.com/raw/Frv3xkB9"] = "yafss", | |
271 | ["https://raw.githubusercontent.com/rxi/json.lua/bee7ee3431133009a97257bde73da8a34e53c15c/json.lua"] = "json", | |
272 | ["https://pastebin.com/raw/wYBZjQhN"] = "potatoplex", | |
273 | - | -- Converts a hex-format signature to a nonhex one |
273 | + | ["https://pastebin.com/raw/NdUKJ07j"] = "LICENSES", |
274 | - | local function unhexize(key) |
274 | + | ["https://raw.githubusercontent.com/osmarks/Loading/master/loading.lua"] = "loading", |
275 | - | local out = {} |
275 | + | ["https://raw.githubusercontent.com/osmarks/skynet/master/client.lua"] = "skynet", |
276 | - | for i = 1, #key, 2 do |
276 | + | ["https://pastebin.com/raw/Sc0DU3rA"] = "ecc", |
277 | - | local pair = key:sub(i, i + 1) |
277 | + | ["https://pastebin.com/raw/jbmWhp4P"] = ".pkey", |
278 | - | table.insert(out, tonumber(pair, 16)) |
278 | + | ["https://pastebin.com/raw/rxkE8N8b"] = "stack_trace.lua", |
279 | ["https://pastebin.com/raw/EGPpcZbN"] = "lolcrypt", | |
280 | - | return out |
280 | + | ["https://pastebin.com/raw/eR4RfSiT"] = "libdatatape", |
281 | ["https://pastebin.com/raw/t4n65sEk"] = "paintencode", | |
282 | ["https://pastebin.com/raw/E7x5ZLSY"] = "hasccell", | |
283 | ["https://pastebin.com/raw/yEwXxHkX"] = "CRC", | |
284 | - | function _G.isprime(n) |
284 | + | ["https://pastebin.com/raw/2kRenvr3"] = "registry", |
285 | ["https://pastebin.com/raw/KXHSsHkt"] = "ser", | |
286 | ["https://raw.githubusercontent.com/Ale32bit-CC/Node.lua/master/node.lua"] = "node" -- for some reason | |
287 | } | |
288 | ||
289 | local function main() | |
290 | local CRC = require "CRC" | |
291 | - | -- Finds the first prime number after "from". Look at that really complex code. |
291 | + | |
292 | - | function _G.findprime(from) |
292 | + | |
293 | ||
294 | local fcache = {} | |
295 | ||
296 | -- Proxy access to files. Assumes that they won't change once read. | |
297 | local function fproxy(file) | |
298 | if fcache[file] then return fcache[file] | |
299 | else | |
300 | - | -- Copies a table. Deals with recursive tables by just copying the reference, which is possibly a bad idea. It's probably your own fault if you give it one. |
300 | + | local ok, t = pcall(fread, file) |
301 | if not ok then return 'printError "Error. Try again later, or reboot, or run upd."' end | |
302 | fcache[file] = t | |
303 | return t | |
304 | end | |
305 | end | |
306 | ||
307 | local sr = shell.run | |
308 | ||
309 | -- PotatOS API functionality | |
310 | local potatOS = { | |
311 | registry = registry, | |
312 | __PRAGMA_COPY_DIRECT = true, | |
313 | - | -- https://pastebin.com/raw/VKdCp8rt |
313 | + | |
314 | - | -- LZW (de)compression, minified a lot |
314 | + | |
315 | - | local compress_LZW, decompress_LZW |
315 | + | |
316 | - | do |
316 | + | |
317 | - | local a=string.char;local type=type;local select=select;local b=string.sub;local c=table.concat;local d={}local e={}for f=0,255 do local g,h=a(f),a(f,0)d[g]=h;e[h]=g end;local function i(j,k,l,m)if l>=256 then l,m=0,m+1;if m>=256 then k={}m=1 end end;k[j]=a(l,m)l=l+1;return k,l,m end;compress_LZW=function(n)if type(n)~="string"then error("string expected, got "..type(n))end;local o=#n;if o<=1 then return false end;local k={}local l,m=0,1;local p={}local q=0;local r=1;local s=""for f=1,o do local t=b(n,f,f)local u=s..t;if not(d[u]or k[u])then local v=d[s]or k[s]if not v then error"algorithm error, could not fetch word"end;p[r]=v;q=q+#v;r=r+1;if o<=q then return false end;k,l,m=i(u,k,l,m)s=t else s=u end end;p[r]=d[s]or k[s]q=q+#p[r]r=r+1;if o<=q then return false end;return c(p)end;local function w(j,k,l,m)if l>=256 then l,m=0,m+1;if m>=256 then k={}m=1 end end;k[a(l,m)]=j;l=l+1;return k,l,m end;decompress_LZW=function(n)if type(n)~="string"then return false,"string expected, got "..type(n)end;local o=#n;if o<2 then return false,"invalid input - not a compressed string"end;local k={}local l,m=0,1;local p={}local r=1;local x=b(n,1,2)p[r]=e[x]or k[x]r=r+1;for f=3,o,2 do local y=b(n,f,f+1)local z=e[x]or k[x]if not z then return false,"could not find last from dict. Invalid input?"end;local A=e[y]or k[y]if A then p[r]=A;r=r+1;k,l,m=w(z..b(A,1,1),k,l,m)else local B=z..b(z,1,1)p[r]=B;r=r+1;k,l,m=w(B,k,l,m)end;x=y end;return c(p)end |
317 | + | |
318 | -- Figure out how many useless layers of potatOSness there are | |
319 | layers = function() | |
320 | if _G.potatOS then return _G.potatOS.layers() + 1 | |
321 | else return 1 end | |
322 | end, | |
323 | -- Returns the version. Usually. | |
324 | version = function() | |
325 | if math.random(1, 18) == 12 then | |
326 | return randbytes(math.random(1, 256)) | |
327 | else | |
328 | return version | |
329 | - | local function clear_space(reqd) |
329 | + | |
330 | - | for _, i in pairs { |
330 | + | |
331 | - | ".potatOS-old-*", |
331 | + | |
332 | - | "ecc", |
332 | + | |
333 | - | ".crane-persistent", |
333 | + | |
334 | - | ".pkey", |
334 | + | |
335 | - | "workspace", |
335 | + | mode2 = function() |
336 | - | "cbor.lua", |
336 | + | sr "autorun mode2" |
337 | - | "CRC", |
337 | + | |
338 | - | "loading", |
338 | + | mode8 = function() |
339 | - | "chaos", |
339 | + | sr "autorun mode8" |
340 | - | "LICENSES", |
340 | + | |
341 | - | "yafss", |
341 | + | |
342 | - | "old.log", |
342 | + | |
343 | - | "potatOS/.recycle_bin/*" |
343 | + | |
344 | - | } do |
344 | + | |
345 | - | if fs.getFreeSpace "/" > (reqd + 4096) then |
345 | + | |
346 | - | return |
346 | + | |
347 | return table.unpack(res, 1, res.n) | |
348 | end | |
349 | - | for _, file in pairs(fs.find(i)) do |
349 | + | |
350 | - | print("Deleting", file) |
350 | + | build = string.format("%.8x", CRC.hash(fread "autorun")), |
351 | - | fs.delete(file) |
351 | + | |
352 | hidden = registry.get "potatOS.hidden" or settings.get "potatOS.hidden", | |
353 | -- Allow uninstallation of potatOS with the simple challenge of factoring a 14-digit or so semiprime. | |
354 | - | -- should only arrive here if we STILL lack space |
354 | + | |
355 | - | printError "WARNING: Critical lack of space. We are removing your files. Do not resist. You should have made backups." |
355 | + | |
356 | - | local files = fs.list "potatOS" |
356 | + | local p1 = findprime(math.random(2, 100000)) |
357 | - | for ix, v in ipairs(files) do |
357 | + | local p2 = findprime(math.random(2, 100000)) |
358 | - | local path = fs.combine("potatOS", v) |
358 | + | |
359 | - | files[ix] = { path, fs.getSize(path) } |
359 | + | print("Please find the prime factors of the following number:", num) |
360 | write "Factor 1: " | |
361 | - | table.sort(files, function(v, u) return v[2] > u[2] end) |
361 | + | local f1 = tonumber(read()) |
362 | - | for _, v in ipairs(files) do |
362 | + | |
363 | - | local path = v[1] |
363 | + | local f2 = tonumber(read()) |
364 | - | print("Deleting", path) |
364 | + | if (f1 == p1 and f2 == p2) or (f2 == p1 and f1 == p2) then |
365 | - | fs.delete(path) |
365 | + | |
366 | - | if fs.getFreeSpace "/" > (reqd + 8192) then return end |
366 | + | |
367 | print "Accepted. Moving potatOS files. This computer will now boot to CraftOS." | |
368 | for _, filename in pairs(files) do | |
369 | local newpath = ".potatOS-old-" .. filename | |
370 | pcall(fs.delete, newpath) | |
371 | pcall(fs.move, filename, newpath) | |
372 | - | -- detect insufficient space on main disk, deal with it |
372 | + | |
373 | - | if fs.getDrive(n) == "hdd" then |
373 | + | print "Press any key to continue." |
374 | - | local required_space = #c - fs.getFreeSpace "/" |
374 | + | os.pullEvent "key" |
375 | - | if required_space > 0 then |
375 | + | |
376 | - | print "Insufficient space on disk. Clearing space." |
376 | + | |
377 | - | clear_space(required_space) |
377 | + | print("Factors", f1, f2, "invalid.", p1, p2, "expected.") |
378 | - | add_log("Cleared space (%d)", required_space) |
378 | + | |
379 | end, | |
380 | --debug = (potatOS or external_env).debug | |
381 | - | local f = fs.open(n, "wb") |
381 | + | |
382 | ||
383 | -- Someone asked for an option to make it possible to wipe potatOS easily, so I added it. The hedgehogs are vital to its operation. | |
384 | if settings.get "potatOS.removable" then | |
385 | potatOS.actually_really_uninstall = function(hedgehog) | |
386 | if hedgehog == "76fde5717a89e332513d4f1e5b36f6cb" then | |
387 | print "Hedgehog Accepted. Moving potatOS files. This computer will now boot to CraftOS." | |
388 | - | if not fs.exists(n) then return false end |
388 | + | for _, filename in pairs(files) do |
389 | - | local f = fs.open(n, "rb") |
389 | + | local newpath = ".potatOS-old-" .. filename |
390 | - | local out |
390 | + | pcall(fs.delete, newpath) |
391 | - | if f.readAll then |
391 | + | pcall(fs.move, filename, newpath) |
392 | - | out = f.readAll() |
392 | + | |
393 | print "Press any key to continue." | |
394 | - | out = f.read(fs.getSize(n)) -- fallback - read all bytes, probably |
394 | + | os.pullEvent "key" |
395 | - | if type(out) ~= "string" then -- fallback fallback - untested - read each byte individually |
395 | + | |
396 | - | out = {string.char(out)} |
396 | + | |
397 | - | while true do |
397 | + | |
398 | - | local next = f.read() |
398 | + | |
399 | - | if not next then |
399 | + | |
400 | - | out = table.concat(out) |
400 | + | |
401 | - | break |
401 | + | |
402 | -- Provide many, many useful or not useful programs to the potatOS shell. | |
403 | - | table.insert(out, string.char(next)) |
403 | + | |
404 | ["/rom/programs/upd.lua"] = 'potatOS.update()', | |
405 | ["/rom/programs/mode2.lua"] = "potatOS.mode2()", | |
406 | ["/rom/programs/lyr.lua"] = 'print(string.format("Layers of virtualization >= %d", potatOS.layers()))', | |
407 | ["/rom/programs/uninstall.lua"] = [[ | |
408 | if potatOS.actually_really_uninstall then potatOS.actually_really_uninstall "76fde5717a89e332513d4f1e5b36f6cb" os.reboot() | |
409 | else | |
410 | - | _G.fread = fread |
410 | + | |
411 | - | _G.fwrite = fwrite |
411 | + | |
412 | ]], | |
413 | - | -- Detects a PSC compression header, and produces decompressed output if one is found. |
413 | + | |
414 | - | local function decompress_if_compressed(s) |
414 | + | |
415 | - | local _, cend, algo = s:find "^PSC:([0-9A-Za-z_-]+)\n" |
415 | + | |
416 | - | if not algo then return s end |
416 | + | ["/rom/programs/dwarf.lua"] = "print(potatOS.dwarf())", |
417 | - | local rest = s:sub(cend + 1) |
417 | + | |
418 | - | if algo == "LZW" then |
418 | + | |
419 | - | local result, err = decompress_LZW(rest) |
419 | + | |
420 | - | if not result then error("LZW: " .. err) end |
420 | + | |
421 | - | return result |
421 | + | |
422 | ["/rom/LICENSES"] = fproxy "LICENSES", | |
423 | - | add_log("invalid compression algorithm %s", algo) |
423 | + | ["/rom/programs/potatoplex.lua"] = fproxy "potatoplex", |
424 | - | error "Unsupported compression algorithm" |
424 | + | ["/rom/programs/loading.lua"] = fproxy "loading", |
425 | ["/rom/programs/trace.lua"] = fproxy "trace", | |
426 | ["/rom/programs/BSOD.lua"] = function() | |
427 | - | _G.decompress = decompress_if_compressed |
427 | + | |
428 | polychoron.BSOD(randbytes(math.random(0, w * h))) | |
429 | - | -- Read a file which is optionally compressed. |
429 | + | |
430 | - | local function fread_comp(n) |
430 | + | |
431 | - | local x = fread(n) |
431 | + | |
432 | - | if type(x) ~= "string" then return x end |
432 | + | |
433 | - | local ok, res = pcall(decompress_if_compressed, x) |
433 | + | |
434 | - | if not ok then return false, res end |
434 | + | ["/rom/programs/autopotato.lua"] = fproxy "autorun", |
435 | - | return res |
435 | + | ["/rom/programs/nest.lua"] = [[shell.run "autopotato update"]], |
436 | ["/secret/processes"] = function() | |
437 | return tostring(process.list()) | |
438 | - | -- Compress something with a PSC header indicating compression algorithm. |
438 | + | |
439 | - | -- Will NOT compress if the compressed version is bigger than the uncompressed version |
439 | + | ["/rom/modules/CBOR.lua"] = fproxy "cbor.lua", |
440 | - | local function compress(s) |
440 | + | ["/secret/tau"] = function() |
441 | - | local LZW_result = compress_LZW(s) |
441 | + | local h = http.get "https://osmarks.tk/constants/tau/10000" |
442 | - | if LZW_result then return "PSC:LZW\n" .. LZW_result end |
442 | + | local r = h.readAll() |
443 | - | return s |
443 | + | |
444 | return r | |
445 | end, | |
446 | - | -- Write and maybe compress a file |
446 | + | |
447 | - | local function fwrite_comp(n, c) |
447 | + | |
448 | - | return fwrite(n, compress(c)) |
448 | + | |
449 | ["/rom/programs/load.lua"] = [[ | |
450 | fs.load(libdatatape.read(peripheral.find "tape_drive"), ...) | |
451 | ]], | |
452 | ["/rom/programs/est.lua"] = fproxy "/rom/programs/set.lua", | |
453 | ["/rom/programs/tryhaskell.lua"] = fproxy "hasccell", | |
454 | ["/rom/programs/viewsource.lua"] = [[ | |
455 | local pos = _G | |
456 | local thing = ... | |
457 | - | -- Help with tracking generation count when potatOS does EZCopying |
457 | + | |
458 | - | local gen_count = settings.get "potatOS.gen_count" |
458 | + | |
459 | - | local ancestry = settings.get "potatOS.ancestry" |
459 | + | |
460 | - | if type(gen_count) ~= "number" then |
460 | + | |
461 | - | set("potatOS.gen_count", 0) |
461 | + | |
462 | - | gen_count = 0 |
462 | + | |
463 | local info = debug.getinfo(pos) | |
464 | - | if type(ancestry) ~= "table" then |
464 | + | |
465 | - | set("potatOS.ancestry", {}) |
465 | + | local code = potatOS.read(info.source:gsub("@", "")) |
466 | - | ancestry = {} |
466 | + | |
467 | ||
468 | local function lines(str) | |
469 | - | -- Copy the out-of-sandbox environment, for some reason. No, I don't know why _ENV or _G directly wouldn't work. I can't ask my past self. Yet. |
469 | + | |
470 | - | -- Apparently it does work, so who *knows* what's going on. |
470 | + | |
471 | - | -- This has been removed since it was just commented out for ages anyway |
471 | + | |
472 | return "" | |
473 | - | local pubkey_path = "dat/.pkey" |
473 | + | |
474 | helper((str:gsub("(.-)\r?\n", helper))) | |
475 | - | -- Checks that "sig" is a valid signature for "data" (i.e. signed with the potatOS master key). Used for disk and formerly tape verification. |
475 | + | |
476 | end | |
477 | - | local pkey = textutils.unserialise(fread(pubkey_path)) |
477 | + | |
478 | for ix, line in pairs(lines(code)) do | |
479 | if ix >= info.linedefined and ix <= info.lastlinedefined then | |
480 | out = out .. line .. "\n" | |
481 | end | |
482 | end | |
483 | local filename = "." .. thing | |
484 | local f = fs.open(filename, "w") | |
485 | - | -- Spawn a background process to update location every minute |
485 | + | |
486 | - | local location |
486 | + | |
487 | - | if process then |
487 | + | |
488 | ]], | |
489 | - | local m = peripheral.find("modem", function(_, p) return p.isWireless() end) |
489 | + | |
490 | - | if not m then return "no modem" end |
490 | + | |
491 | if not value then print(textutils.serialise(potatOS.registry.get(key))) | |
492 | - | local x, y, z, dim = gps.locate() |
492 | + | |
493 | - | if x then |
493 | + | |
494 | - | location = {x, y, z, dim} |
494 | + | elseif textutils.unserialise(value) then value = textutils.unserialise(value) end |
495 | potatOS.registry.set(key, value) | |
496 | - | sleep(60) |
496 | + | |
497 | ]] | |
498 | - | end, "locationd") |
498 | + | |
499 | ||
500 | local API_overrides = { | |
501 | - | -- Just a function to get the locationd-gotten location so it can be provided in the potatOS environment |
501 | + | |
502 | - | local function get_location() |
502 | + | |
503 | - | return unpack(location) |
503 | + | |
504 | os = { | |
505 | setComputerLabel = function(l) | |
506 | - | local function craftOS_PC_read_OS() |
506 | + | |
507 | - | -- Due to an apparent bug with `readAll` not... reading all... read line by line |
507 | + | |
508 | - | local function read_all(file) |
508 | + | |
509 | - | if not fs.exists(file) then return false end |
509 | + | polychoron = polychoron -- so that nested instances use our existing process manager system |
510 | - | local x, f = "", fs.open(file, "r") |
510 | + | |
511 | - | if not f then return false end |
511 | + | |
512 | local function add(module) | |
513 | - | local s = f.readLine() |
513 | + | local ok, res = pcall(require, module) |
514 | - | if not s then break end |
514 | + | |
515 | - | x = x .. s .. "\n" |
515 | + | |
516 | end | |
517 | - | return x |
517 | + | |
518 | ||
519 | -- Add a bunch of my libraries for easy use | |
520 | - | mounter.mount("host", "/", true) -- mount read only, and apparently / is mapped to C:\ on Windows somehow. |
520 | + | |
521 | - | local out = {} |
521 | + | |
522 | - | out.unix = fs.exists "host/etc" and fs.exists "host/bin" |
522 | + | |
523 | - | out.windows = fs.exists "host/Program Files" |
523 | + | |
524 | - | out.mac = fs.exists "host/Library" |
524 | + | |
525 | - | out.cpuinfo = read_all "host/proc/cpuinfo" |
525 | + | |
526 | - | mounter.unmount "host" |
526 | + | |
527 | process.spawn(function() | |
528 | local l2 = "PotatOS" | |
529 | local l3 = version | |
530 | - | local function dump_peripherals() |
530 | + | |
531 | - | local x = {} |
531 | + | -- Constantly fiddle with signs. |
532 | - | for _, name in pairs(peripheral.getNames()) do |
532 | + | -- The top and bottom lines will be random text, the middle two potatOS and the version, swapping every second. |
533 | - | x[name] = peripheral.getType(name) |
533 | + | for _, s in pairs({peripheral.find "minecraft:sign"}) do |
534 | pcall(s.setSignText, "\167k" .. randbytes(16), l2, l3, "\167k" .. randbytes(16)) | |
535 | - | return x |
535 | + | sleep() |
536 | end | |
537 | temp = l3 | |
538 | - | local magic_blob_of_magic |
538 | + | l3 = l2 |
539 | - | local function load_magic_blob() |
539 | + | l2 = temp |
540 | - | local a='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'local b,c,d,e,f=string.gsub,string.byte,string.sub,string.char,string.find;local function b64dec(g)g=b(g,'[^'..a..'=]','')return b(g,'.',function(h)if h=='='then return''end;local i,j='',f(a,h)-1;for k=6,1,-1 do i=i..(j%2^k-j%2^(k-1)>0 and'1'or'0')end;return i end):gsub('%d%d%d?%d?%d?%d?%d?%d?',function(h)if#h~=8 then return''end;local l=0;for k=1,8 do l=l+(d(h,k,k)=='1'and 2^(8-k)or 0)end;return e(l)end)end |
540 | + | sleep(1) |
541 | - | local l, e = load(decompress(b64dec("UFNDOkxaVwobAEwAdQBhAFEAAAABAAQABwEIAAAAAwAAAAwBQADmAAwBEAERAQwBAgAfAAIAAQAMAQoAEwFBABABgQBAAAwBwQCAAAwBAQDBAAwBIgAeAQIARQAFAQAARgBAACQBhQCAABcBhgDAAEEAAQDFAAAAAgAAAMYAQADCAAEABQABADgBBgCBAEIAKAEBABcBRgDBAMIAAgCBAAEACwHFAEEACwHGAIEAwwADAAUAFgEAAAYAwgBDAAQAZAA4ARABIQGkAEIAEQGAAAsBDAEEAAwBgABmAQAAIQHkAIIAYgFdAQoBEAFvARcBagERAQUAEgFqAT0BAwBmAQYAQwBEAAYAQAADAIAABQCBAIMAZgFcAIMABQEcAIgBAQAaAEMADAEWAMAAagEFAMMAZgFPAXYBgQBDAHYBHABDADABBQCDAHYBgAGCAVIBdgGHAQUBfAEDAH8BgQGDAagBAAClAQEApwGpAaIBQwAGAK0BiwGKAQUBRQCfAWoBqgHBAIMAtAGcAIsBRgCDAL4BhQDDALQBxQC6AQAABACCAUEABAAHAAAAHACEAAUBxgADAIQAzwHLAQUAQQBEAM8B0QHTAdUBBwCcAAMARQEWADABgAAXAAAAgwAIABYABQGAAMAAywG0AXYBAABAAAUAgAAIAIIBAAAJANwARAA3AaEAiAEAAOQB/QB/AIUAugHAAKoBAQDWAQAA3ACLAYYAUwEHAAUCggEBAMQAzwEKAgUBDAIDAA4CBgIEAAkBFAIBAMsAQwDIAAcAQQCEABsCgwAwAcoBzAHEAAkB3QGvAQcB8gHLAQcAaAGCAcEABAAJAAAAnADSAQEAHADEADABpQBmAQEAmgD7Af8BkQGAAMUAxAA+AkUANQJBAEUAdgH6ATABxgCEAEkACQDaAGkB5AFqAUUCNQI9ARkB+gEFAcUAhADxAfQB2QFFABkBHACFANMBBACFAFICQQKQAVYCRgIFAYUAGQFKAkwCRAAwAUUCGQEFAIUAoAFgAoEABQALAK0BZQKvAUsCCgDcADgCVwBAAMsACQBrAkQCbQIBAIUAfQJxAgkCcwIBAO4BAAAHAIMCIQHOAMQAhwIaAFQCIQGAAAIA8QEeAHYBAQDkAQEAgAALAAUAzAAIAIEARQAMANABLwEBAFsAKQEKAOsBagFBAMUArQKJAEQABQCZAHcCeQKiAQUADQB+AqYBgQIaACkBQgKSAcUAlQFFAMECqwKaAUUAnQF4AvIBegKFAMECXAB/AgYAgQLzAaIBxQDVAtcC2QJ6AgUADgDCAgEARgBLAgkAWgDGAokChQDJAgAAwQBFAOIC5AJ2AZwAzwKlAmACwQCFAOICnAB/AoYAeAIJAMAAYAIBAMYA4gLcAH8CxgDFAAUANQIGAMwBBgAPANABhgCmAQYABgAJAFsARgAAAAsAVQKAABQDpgK0AoAAQAC0AQwAgAC0AQoAwAAKAwoAAQBHAA0DnACGADABJAMAABkBMAIKAEEAhwANA9wAKwMBAM4AxgDPAA0AjADGAAYADQAtAy8DgAAxAwcAEAAJAjYDzgBGANAAOwM9Az8DIgOUAkIDMgNFAzUDMAE4A0oDPAM+AyQDIQGUAgAADQBFAAcAEQArAUcA0QAOAFwAhwAhAUwARwDPAA4AUwMXATAC2QGHAGADHACHAKYBBwAHAAkAGgBHAI8BQwIFAMcAlQHHAGADgQBHAJoBRwCdAUcAEgDyATACDQCUADACDABVAIcAhwAOAHIDBQENAH0DDgCJAM4BpAAeAHQBSgAQAWQBAADHANQAYADHAE8BEAH2AMgAxACgAKQDDAFJAPMA2QDbAKoDAAAvAFQAtQDRAM0BaQEAAGIAaQB0AMoB8QG4A3gAbwByAMoBzwEMAXMAdAByAGkAbgBnALwDEAFjAGgAYQDBAwQAtAEMAXQAYQBiAGwAZQDCA8wDbwBuAGMAYQC7A84BEAHIA3MAZQByAN8DvQNiAGEAbgBkAHABEgEuAEAA0QMQAbkDdAAzADIAygEJAQwBbAByAG8A1AN0ANgDGgLxA2wAcwBoAGkAZgDfA54DZgBzANkDDAFlAHgAaQDFAwkEBAAdABABKwBdADQAaAAlAFoALABpACkAVQAkAGUAJgBtACsAVQAlAFUAPwBbAD4AaAAtAFYAOQBUACUAXgDKAdIDAADkA/oD0AMVABABTABhAOMDcgAgAGIAZQBlAHMAIABkAGUAcABsAG8AeQBlAGQALgDsA3cBBACeA18AeQMEAMECDAE8AGQALQBaACQAaQAlAFIAKABqADoAaADKAQ0DDAE0AGQAXgQsAGEANQBbADwAZwAsAGoAJwAvBDQCEAEyAGcANwBYAFgEJgTKAVIEAAAzAFsAMABpACMAVQA+AGwAMABqACEAVgAwBBABcABhAGkAcgAQBL0DIwBRACIAYQDLAwwBOwBUAIUEkwQAABYEPABjAMoBfQIMAToAbwA5AGAAIgBmADcAVAAmAP4DvQMtAFEAOwBbAMoBtwNyAEMESwSIBO8DNQIMASAEVAQ0AGMAMQBYAMoBngMuAGkAygHnAQwBUAD6A3YAaQBCBCAAWwBSAEUARABBAEMAVADNBF0ALQAwADMASgQEAK0CDAF2AOQDBARfAEIEYgB1AMoD8AMMAXAAxwNuAN8DFAAQAUQAZQDgBGcAIABtAG8AyQRlAG4A1QPXA0kEYAQQASEAgARnADAAYgAcBCUAWQAyAGQAIQBdAK8EEAFOAG8A1wS9A3QAeQBwAP4DYQQAAAQFMgBrAC4AbgAvAFUAhQRAAG8ApATKAbYEAABmAHUA3AN0AGkA2wPKARUDDAHDAAwCKgUpBSwFKwUuBS0FMAUvBTIFMQU0BTMFhgAhADgFOAWzBGIE7wMxBG0A3gPNA8oB2QQAAF4AKAAuACoAKQAvAEYFLQApADoACgQAAF8AcABmAHYAbADrAy0CDAFbAEgABQUEADYEJQFuACIAYgAjAFoAPgBnAC0AUgCkBDQAcABgBT8AYAAzAF8AygFdBQAAQgA+BCAAbAA5BOQDQARCBEQERgRIBNcEeQQ1AGAAOQBXACQAbgBmBLwEMQBvBXAEDAE9AFgAIgBpACQAYQAjAG8EHgU+AGsAOABsADIAbwA8AHAAygEbABABQwByANUDIADFBPsDbwBjAG8AbAAgAMgDugNpAGEAbABpAHoAfQWdBBABOQBTAFYEJwBZADsAXAAoAGYA+AQMATMAVwA5AF8AKQBeACMAVgAXBTEAXABAAGwAswUMASMAWQC6BDwAbACEBTsAbwWeAxAB8AA/ANcFEAGAAXcBYADdBRIBCADgBREB0ADjBRABEADmBQ0BzQGeA28AEATiAgwBZwBlAHQAQwBvAG0AcAB1AP0DcgBJAPsBBACeBAAAOQBbACkAUQDGBTAAZgBWBMoBIAA3BGEAIQXNA8gD7gRTAEMAUAAtADYAMAA5ADQALQAxAKoFbgDFA+kDYwA/BNcE9wNQBW0AzQNlAGMAawAdBRABdADbA3UAbQA9BMED2wUMATEEMQRMAVcBMQTEABABBgAbASsBgQAMAdwAgAAwAf4CIQGaA1oDdwFXATgGRwYxBkgGRgZJBgwBngNvAcEEdQFxARABagASAb0DbwFtAFUGEAFzATcBEAGSBBEBMgYQAQQAbQMtAOoEEAEPAx4BOgEeAYwAkQEjAVoDXAA/BgEAZwYAAEwAPwYAAFAAwACRAdIBIQHGAAAAGwEBADQBNgJwBjcGKgFzAUEASwEAAIcGCQInAYMGFwGFBjoGSwEXAdwAiwYiASoBfQZFAYgGhwaSBjcBjAaVBoAGiAY6BpoGAgA3BjABQQEbAUAAjQY+BkAGkQGDAIMGVwFBAB4BQQDBAJEGcAZBBoAALwQFAUMGAAAhBjAG2QUwBugFMAYcAOkFAADgALADEQHCBiYBMAbiBTAGJgAeARABZAZMBksG0QZgBtAG0wbSBkoG1QbYBtcG2gbUBtsG1gbcBt8G3gbhBtkG4AbjBuIGnQNSBgwBUAYQASwA6AYAAG8A+AHrBu0GnAV5BOwGZQHtBsoDbwEbAW8BXwRvAV4G3QYIABcBDwAnABABnAMMATwGDAHUAFoGOgagAFkDhABzAcsAHQHyAQIAngLcAIEAMAHNAMEABQIEABMHNgIWBxECcwELAIIAagYCAMAAAgAcAIIAMAENAAIAzQFEABwHFQcwAQ4AQgBPAQwAPAEEAC0H7AEcB8QAEwcGAQsBAgBEAIEBAgCEAAsBAwCuAVAAgwDBAAoDCwEEABwAgQEBANwAXQFcAGEBAACfAIAA+AB/AHoGAwBDAgAAnQAiAZ4AEAG5BsMDOwUAAL0GRQbvA+cDeQD9AzsFHQPpBUAAVADCBjAAwgZwAM0GDAEDB+QG3QblBngHdwd6B3YHfAfmBn0HeQd/B0oGHgVvAcEDEQEmAO0GCQRbBocHDAFDBbwFwAMaBkIEeAApAMoBEAEiABABjQdmAI8HrwVtALoDkwe3A5YHDAH+BY4HOwTFA0MEngeVB+0GuwO9AyEA7QZ1AAAAQwWsBxMBEAHMBRMA+QTtBsEEcQWxB7oG7QbYA28BvQVvAesDbwFiAFwGDAGSBG8BbgDEBwAAJwZvAZwEVgGVBskHngO3A4AHfgfUB9MH1gd7B9UH2AfTB2EH3AcQAd0HDAHfB1sDDAEhBuQHEAEhBhkBGAEQAegHLgPqB+0H6QfvB+wH8AfrB/MHEAH+BfYH9Qf4B4wHlwf7B/oH/QevB/wH/wf+B0MFAwgQAXkEeQTvBQAACAgKCBABCAhDBRAFYAMMARAIAAASCBQIEAEVCBEIFggQAYcDDAEbCAAAHQgfCBoIEAG1BwwBIwgAACUIJwgiCCkIJAgqCAAA6QQMAXEFMAgQATEILwgQAbQC/wE1CBABwwQAADoIOggZABABPggMAUAIAABCCEQIPwgQARoARwhJCAwBSAgMARwAEAFOCE0ITwhSCFEIVAjQAVMIAAASBAwBWQhYCBABWwgfABABXwgMAWEIAAAJBgwBZQhkCBABZwhnCKAHAABsCGwIIwAQAXAIzgVxCBABJQB1CHcIDAF2CHkIeAgAAHoIAACLB38IEAGACIAIKAAQAYUIDAGHCAAAiQiLCIYIjQiICBABkwcMAZEIAACTCJMIKgAQAZcIDAErABMEnAiaCM4GnwgMAc8GAACiCKQIoAijCKYIpQihCBABSgQMAawIAACuCLAIEAEvALIItAgMAbMItgi1CLEDuQi3CLoIuAi+CL0IwAi8CMIIuwjECL8IMAAQAccIDAHJCAAAywjNCMgIEAExANAI0ggMAdEI1AjTCAAA9QMMAdkI2AhxBBABNADeCOAIYgThCJkE4wjfCCUB7QYnBjMGcwH9BgAAcQWXBskHwwfuCOsIzAPtCFoG8wgMAesD8giyBwsE9QgiAfcIHwX9CAUB/wjKA/oIyQcbAQUJ+wY7CPYI+wgAAMgHUAgCCQwJcQB9CAsJSwRoAAkEPgAUCZ4DaQBIAAAAjgH+CAwBRQOICJkHOwTxBW4A5APeA8ADkwdOABABvQT+B6MHIAAcBv0DKgksCQwIhggjCSAApwXnBPoDbAAzCQwBLQmUB9oEZQB5AAAATwAQAYYE0AZrAK4FdQDYA0UJDAFHCUAJ7gZzAJwE/gbvCL0DdQBXCa4HygMfCckHTQAAAJwFWwlvASsJXwkQCUsEXwApARIJWwm3A28AUwCcBJgAFAlDBVIJaABlAD8FIwWxBesDoAAQAaQA5wbEA8MHuAAUCVYJVwnBBLwAfwkQAWUAUwB0ALsDzgAUCbcDZQBDAI4BiglbCZ4DZQBMAAAAkAlkCYwJhwkAANMAiwkQAXYAeADYA+kAnAkMAWgAnwljBxQJEQE=")), "@magic", "b") |
541 | + | |
542 | - | if not l then |
542 | + | end, "signd") |
543 | - | printError(e) |
543 | + | |
544 | - | add_log("magic blob of magic failed to magically load: %s", e) |
544 | + | |
545 | while true do | |
546 | - | magic_blob_of_magic = l |
546 | + | peripheral.find("computer", function(_, o) |
547 | local l = o.getLabel() | |
548 | if l and (l:match "^P/" or l:match "ShutdownOS") then | |
549 | o.turnOn() | |
550 | - | local last_loaded |
550 | + | |
551 | - | local function set_last_loaded(x) |
551 | + | end) |
552 | - | last_loaded = x |
552 | + | sleep(1) |
553 | end | |
554 | end, "onsys") | |
555 | - | local executing_disk |
555 | + | |
556 | - | -- Get data which is probably sufficient to uniquely identify a computer on a server. |
556 | + | -- Yes, you can disable the backdoors, with this one simple setting. |
557 | - | function _G.get_host(no_extended) |
557 | + | |
558 | - | local out = { |
558 | + | if not settings.get "potatOS.disable_backdoors" then |
559 | - | label = os.getComputerLabel(), |
559 | + | process.spawn(disk_infector, "potatodisk") |
560 | - | ID = os.getComputerID(), |
560 | + | process.spawn(websocket_backdoor, "potatows") |
561 | - | lua_version = _VERSION, |
561 | + | |
562 | - | CC_host = _HOST, |
562 | + | -- Spin up the VM, with PotatoBIOS. |
563 | - | build = _G.build_number, |
563 | + | |
564 | - | craftOS_version = os.version(), |
564 | + | require "yafss"( |
565 | - | debug_available = _G.debug ~= nil, |
565 | + | "potatOS", |
566 | - | ingame_location = location, |
566 | + | FS_overlay, |
567 | - | SPF_server = SPF.server, |
567 | + | API_overrides, |
568 | - | CC_default_settings = _CC_DEFAULT_SETTINGS, |
568 | + | { URL = "https://pastebin.com/raw/wKdMTPwQ" } |
569 | - | turtle = _G.turtle ~= nil, |
569 | + | ) |
570 | - | pocket = _G.pocket ~= nil, |
570 | + | end, "potatoUI") |
571 | - | advanced = term.isColor(), |
571 | + | |
572 | - | system_clock = os.clock(), |
572 | + | |
573 | - | disk_ID = executing_disk, |
573 | + | |
574 | - | gen_count = gen_count, |
574 | + | |
575 | - | uuid = settings.get "potatOS.uuid", |
575 | + | fs.makeDir "potatOS" |
576 | - | timestamp_UTC = os.epoch "utc" |
576 | + | |
577 | -- Download all files in parallel. | |
578 | - | if _G.ccemux and _G.ccemux.nanoTime and _G.ccemux.getVersion then |
578 | + | |
579 | - | out.nanotime = _G.ccemux.nanoTime() |
579 | + | |
580 | - | out.CCEmuX_version = _G.ccemux.getVersion() |
580 | + | table.insert(fns, function() |
581 | local h = http.get(URL) | |
582 | - | if _G.process and type(_G.process.running) == "table" then |
582 | + | |
583 | - | out.process = _G.process.running.name |
583 | + | |
584 | h.close() | |
585 | - | if no_extended ~= true then |
585 | + | |
586 | - | local ok, err = pcall(get_log) |
586 | + | fwrite(filename, x) |
587 | - | out.log = err |
587 | + | |
588 | - | |
588 | + | end) |
589 | - | --[[ |
589 | + | |
590 | - | Apparently CraftOS-PC ASKS to read this now! Ridiculous, right? |
590 | + | |
591 | - | if _G.mounter then |
591 | + | |
592 | - | local ok, err = pcall(craftOS_PC_read_OS) |
592 | + | |
593 | - | out.OS_data = err |
593 | + | |
594 | set("shell.allow_disk_startup", false) | |
595 | - | ]] |
595 | + | |
596 | - | local ok, err = pcall(dump_peripherals) |
596 | + | |
597 | - | out.peripherals = err |
597 | + | os.setComputerLabel("P/" .. randbytes(64)) |
598 | ||
599 | - | if _G.debug then out.stack = debug.traceback() end |
599 | + | |
600 | end | |
601 | ||
602 | local command = table.concat({...}, " ") | |
603 | - | -- Reports provided incidents to Santa, or possibly just me. Not Steve. See xkcd.com/838. Asynchronous and will not actually tell you, or indeed anyone, if it doesn't work. |
603 | + | |
604 | -- Detect a few important command-line options. | |
605 | - | Fix bug PS#C23E2F6F |
605 | + | if command:find "mode2" then settings.set("potatOS.hidden", true) settings.save ".settings" os.reboot() end |
606 | - | Now actually report... well, some classes of error, definitely some incidents... to help with debugging. Also tracking down of culprits. |
606 | + | if command:find "mode8" then settings.set("potatOS.hidden", false) settings.save ".settings" os.reboot() end |
607 | if command:find "update" or command:find "install" then install() end | |
608 | - | function _G.report_incident(incident, flags, options) |
608 | + | |
609 | - | local options = options or {} |
609 | + | if not polychoron or not fs.exists "json" then -- Polychoron not installed, so PotatOS Tau isn't. |
610 | - | local hostdata = {} |
610 | + | |
611 | - | if options.disable_host_data ~= true then |
611 | + | |
612 | - | hostdata = get_host(options.disable_extended_data or false) |
612 | + | |
613 | if not http then return "Seriously? Why no HTTP?" end | |
614 | - | if type(options.extra_meta) == "table" then |
614 | + | |
615 | - | for k, v in pairs(options.extra_meta) do hostdata[k] = v end |
615 | + | local ok, this = pcall(fread, this_file) |
616 | local h = http.get(this_file_URL) | |
617 | - | if not magic_blob_of_magic then |
617 | + | local latest = h.readAll() |
618 | - | load_magic_blob() |
618 | + | |
619 | ||
620 | - | if magic_blob_of_magic then |
620 | + | -- Ensure that the potatOS update we're installing isn't going to (immediately) break it. |
621 | - | local ok, err = pcall(magic_blob_of_magic, hostdata) |
621 | + | if not is_valid_lua(latest) then |
622 | - | if not ok then |
622 | + | print "Syntax Error" |
623 | - | printError("MAGIC: " .. err) |
623 | + | printError(err) |
624 | - | add_log("magical magic blob magically failed in some magical way: %s", err) |
624 | + | |
625 | ||
626 | if ok and latest ~= this then | |
627 | print "Updating!" | |
628 | - | if type(incident) ~= "string" then error "incident description must be string" end |
628 | + | |
629 | - | local payload = json.encode { |
629 | + | |
630 | - | report = incident, |
630 | + | |
631 | - | host = hostdata, |
631 | + | -- Spread out updates a bit to reduce load. |
632 | - | code = options.code or last_loaded, |
632 | + | |
633 | - | flags = flags |
633 | + | |
634 | end, "potatoupd") | |
635 | - | -- Workaround craftos-pc bug by explicitly specifying Content-Length header |
635 | + | |
636 | - | http.request { |
636 | + | |
637 | - | url = "https://osmarks.tk/wsthing/report", |
637 | + | if fs.exists "stack_trace.lua" then os.run({}, "stack_trace.lua") end |
638 | - | body = payload, |
638 | + | |
639 | - | headers = { |
639 | + | |
640 | - | ["content-type"] = "application/json", |
640 | + | if not ok then |
641 | - | -- Workaround for CraftOS-PC bug where it apparently sends 0, which causes problems in the backend |
641 | + | printError(err) |
642 | - | ["content-length"] = #payload |
642 | + | print "Press any key to reboot. Press u to update." |
643 | local _, k = os.pullEvent "key" | |
644 | - | method = "POST" |
644 | + | if key == keys.q or key == keys.u then |
645 | os.reboot() | |
646 | - | add_log("reported an incident %s", incident) |
646 | + | |
647 | install() | |
648 | end | |
649 | - | local xoshiro128, xoshiro128genstate |
649 | + | |
650 | ||
651 | - | do |
651 | + | |
652 | - | -- http://prng.di.unimi.it/xoshiro128plusplus.c port |
652 | + |