-- install.lua — installs OpenOS Yaru over a standard OpenOS (or onto any disk). -- Needs an internet card. Downloads yaru.pack and writes it straight to the -- chosen disk while it streams in (nothing big is kept in memory). -- -- wget http:///install.lua /tmp/install.lua -- /tmp/install.lua [url-of-yaru.pack] [-y] [--disk=N|address|label] -- -- Keeps: everything in /home that already exists, your /etc/rc.cfg (the new -- one is written as /etc/rc.cfg.yaru), and all files Yaru does not ship. local component = require("component") local computer = require("computer") local shell = require("shell") local term = require("term") local DEFAULT_URL = "http://vpn.itrubin.com/yaru.pack" local args, opts = shell.parse(...) local url = args[1] or DEFAULT_URL if url == "" then io.stderr:write("usage: install [url of yaru.pack] [-y] [--disk=N|address|label]\n") return 1 end if not component.isAvailable("internet") then io.stderr:write("install: an internet card is needed\n") return 1 end local internet = require("internet") local function ask(q) if opts.y then return true end io.write(q .. " [Y/n] ") local a = (io.read() or ""):lower() return a == "" or a == "y" or a == "yes" or a == "д" or a == "да" end -- ------------------------------------------------------------ target disk local boot, tmp = computer.getBootAddress(), computer.tmpAddress() local disks = {} for addr in component.list("filesystem") do local p = component.proxy(addr) if addr ~= tmp and not p.isReadOnly() then disks[#disks + 1] = {addr = addr, proxy = p, label = p.getLabel() or "", free = p.spaceTotal() - p.spaceUsed(), total = p.spaceTotal(), os = p.exists("/init.lua") and p.exists("/lib/core") } end end table.sort(disks, function(a, b) return (a.addr == boot) and b.addr ~= boot or (a.addr ~= boot) == (b.addr ~= boot) and a.addr < b.addr end) if #disks == 0 then io.stderr:write("install: no writable disk\n") return 1 end print("OpenOS Yaru installer") print("Disks:") for i, d in ipairs(disks) do print(string.format(" %d) %s %-12s %4d KB free%s%s", i, d.addr:sub(1, 8), d.label:sub(1, 12), d.free // 1024, d.os and " (OpenOS)" or "", d.addr == boot and " <- this computer boots from it" or "")) end -- --disk=N (number in the list), an address prefix or a label picks the disk; -- otherwise it is asked (with -y: the boot disk) local pick = 1 if opts.disk then pick = tonumber(opts.disk) if not pick or not disks[pick] then pick = nil for i, d in ipairs(disks) do if d.addr:sub(1, #opts.disk) == opts.disk or d.label == opts.disk then pick = i break end end end elseif #disks > 1 and not opts.y then io.write("Install onto disk [1]: ") pick = tonumber(io.read() or "") or 1 end local disk = disks[pick] if not disk then io.stderr:write("install: no such disk\n") return 1 end local fs = disk.proxy -- ------------------------------------------------------------ download + unpack local handle, reason = internet.request(url) if not handle then io.stderr:write("install: " .. tostring(reason) .. "\n") return 1 end local buf = "" local stream_ok = true local function more() local ok, chunk = pcall(handle) if not ok then stream_ok = false; error("download failed: " .. tostring(chunk), 0) end if not chunk then return false end buf = buf .. chunk return true end local function line() while true do local nl = buf:find("\n", 1, true) if nl then local l = buf:sub(1, nl - 1) buf = buf:sub(nl + 1) return l end if not more() then return nil end end end local head = line() local nfiles, total = (head or ""):match("^YARUPACK 1 (%d+) (%d+)$") if not nfiles then io.stderr:write("install: " .. url .. " is not a Yaru pack\n") return 1 end nfiles, total = tonumber(nfiles), tonumber(total) print(string.format("%d files, %d KB, onto %s %s", nfiles, total // 1024, disk.addr:sub(1, 8), disk.label)) if disk.free + (disk.os and 400 * 1024 or 0) < total then print("Warning: the disk may be too small.") end if not ask("Install now? Existing system files are replaced, /home is kept.") then print("Cancelled.") return end local done, bytes, kept = 0, 0, 0 local lastPct = -1 local function progress() local pct = total > 0 and bytes * 100 // total or 100 if pct ~= lastPct then lastPct = pct local _, y = term.getCursor() term.setCursor(1, y) io.write(string.format("Installing... %3d%% (%d of %d files)", pct, done, nfiles)) end end local ok, err = pcall(function() while true do local l = line() if not l then error("the download ended early", 0) end local kind = l:sub(1, 1) if kind == "E" then break elseif kind == "D" then fs.makeDirectory(l:sub(3)) elseif kind == "F" then local size, path = l:match("^F (%d+) (.+)$") size = tonumber(size) local target = path local skip = false if path:sub(1, 6) == "/home/" and fs.exists(path) then skip = true; kept = kept + 1 end if path == "/etc/rc.cfg" and fs.exists(path) then target = "/etc/rc.cfg.yaru" end local fh if not skip then local dir = target:match("^(.*)/[^/]*$") if dir and dir ~= "" then fs.makeDirectory(dir) end local reason2 fh, reason2 = fs.open(target, "w") if not fh then error(target .. ": " .. tostring(reason2), 0) end end local left = size while left > 0 do if #buf == 0 and not more() then error("the download ended early", 0) end local n = math.min(left, #buf, 8192) if fh then fs.write(fh, buf:sub(1, n)) end buf = buf:sub(n + 1) left = left - n bytes = bytes + n end if fh then fs.close(fh) end done = done + 1 progress() end end end) pcall(handle.close) print() if not ok then io.stderr:write("install failed: " .. tostring(err) .. "\n") io.stderr:write("Run it again; files already written are simply replaced.\n") return 1 end if (fs.getLabel() or "") == "" then pcall(fs.setLabel, "Yaru") end computer.setBootAddress(disk.addr) print(string.format("Done: %d files written%s.", done - kept, kept > 0 and (", " .. kept .. " of your files in /home kept") or "")) if fs.exists("/etc/rc.cfg.yaru") then print("Your /etc/rc.cfg was kept (the new one is /etc/rc.cfg.yaru).") end if ask("Reboot into Yaru now?") then computer.shutdown(true) end