openos improvements

1. if /home is readonly, a helpful message is displayed tell the user to run install
2. remove -i from `cp` alias because a bunch of people complain about it
3. `install` now does not clobber /etc/rc.cfg nor /home/.shrc
This commit is contained in:
payonel 2020-05-21 00:36:09 -07:00
parent 4857395e6a
commit 09505e6cff
8 changed files with 91 additions and 60 deletions

View File

@ -1 +1 @@
{label = "OpenOS", reboot=true, setlabel=true, setboot=true} {label = "OpenOS", reboot=true, setlabel=true, setboot=true, noclobber={"etc/rc.cfg","home/.shrc"}}

View File

@ -30,7 +30,7 @@ options =
P = options.P, P = options.P,
v = options.v, v = options.v,
x = options.x, x = options.x,
skip = options.skip, skip = {options.skip},
} }
return transfer.batch(args, options) return transfer.batch(args, options)

View File

@ -1,6 +1,4 @@
local computer = require("computer") local computer = require("computer")
local shell = require("shell")
local options local options
do do
@ -12,21 +10,23 @@ do
options = basic(...) options = basic(...)
end end
if not options then return end if not options then
return
end
if computer.freeMemory() < 50000 then if computer.freeMemory() < 50000 then
print("Low memory, collecting garbage") print("Low memory, collecting garbage")
for i=1,20 do os.sleep(0) end for i = 1, 20 do
os.sleep(0)
end
end end
local cp, reason = loadfile(shell.resolve("cp", "lua"), "bt", _G) local transfer = require("tools/transfer")
assert(cp, reason) for _, inst in ipairs(options.cp_args) do
local ec = transfer.batch(table.unpack(inst))
local ok, ec = pcall(cp, table.unpack(options.cp_args)) if ec ~= nil and ec ~= 0 then
assert(ok, ec) return ec
end
if ec ~= nil and ec ~= 0 then
return ec
end end
print("Installation complete!") print("Installation complete!")
@ -44,7 +44,7 @@ end
if options.reboot then if options.reboot then
io.write("Reboot now? [Y/n] ") io.write("Reboot now? [Y/n] ")
if ((io.read() or "n").."y"):match("^%s*[Yy]") then if ((io.read() or "n") .. "y"):match("^%s*[Yy]") then
print("\nRebooting now!\n") print("\nRebooting now!\n")
computer.shutdown(true) computer.shutdown(true)
end end

View File

@ -24,7 +24,7 @@ options =
i = options.i, i = options.i,
v = options.v, v = options.v,
n = options.n, -- no clobber n = options.n, -- no clobber
skip = options.skip, skip = {options.skip},
P = true, -- move operations always preserve P = true, -- move operations always preserve
r = true, -- move is allowed to move entire dirs r = true, -- move is allowed to move entire dirs
x = true, -- cannot move mount points x = true, -- cannot move mount points

View File

@ -32,3 +32,7 @@ for _,line in ipairs(lines) do
io.write(borders[2][1], " ", line, (" "):rep(maxLine - #line + 1), borders[2][3], " \n") io.write(borders[2][1], " ", line, (" "):rep(maxLine - #line + 1), borders[2][3], " \n")
end end
io.write(borders[3][1] .. string.rep(borders[3][2], maxLine + 2) .. borders[3][3] .. "\n") io.write(borders[3][1] .. string.rep(borders[3][2], maxLine + 2) .. borders[3][3] .. "\n")
if require("filesystem").get("home").isReadOnly() then
io.write("\27[33mNote: Your home directory is readonly. Run `install` and reboot.\27[m\n")
end

View File

@ -20,7 +20,6 @@ shell.setAlias("cls", "clear")
shell.setAlias("rs", "redstone") shell.setAlias("rs", "redstone")
shell.setAlias("view", "edit -r") shell.setAlias("view", "edit -r")
shell.setAlias("help", "man") shell.setAlias("help", "man")
shell.setAlias("cp", "cp -i")
shell.setAlias("l", "ls -lhp") shell.setAlias("l", "ls -lhp")
shell.setAlias("..", "cd ..") shell.setAlias("..", "cd ..")
shell.setAlias("df", "df -h") shell.setAlias("df", "df -h")

View File

@ -27,7 +27,7 @@ local utils
local rootfs = fs.get("/") local rootfs = fs.get("/")
if not rootfs then if not rootfs then
io.stderr:write("no root filesystem, aborting\n"); io.stderr:write("no root filesystem, aborting\n")
os.exit(1) os.exit(1)
end end
@ -86,11 +86,13 @@ for dev, path in pairs(devices) do
io.stderr:write("Cannot install to " .. options.to .. ", it is read only\n") io.stderr:write("Cannot install to " .. options.to .. ", it is read only\n")
os.exit(1) os.exit(1)
end end
elseif specified or elseif
not (source_filter and address:find(source_filter, 1, true) == 1) and -- specified for source specified or
not target_filter and not (source_filter and address:find(source_filter, 1, true) == 1) and -- specified for source
address ~= tmpAddress then not target_filter and
table.insert(targets, {dev=dev, path=install_path, specified=specified}) address ~= tmpAddress
then
table.insert(targets, {dev = dev, path = install_path, specified = specified})
end end
end end
@ -105,11 +107,11 @@ for dev, path in pairs(devices) do
local install_path = dev == source_filter_dev and options.from or path local install_path = dev == source_filter_dev and options.from or path
local specified = source_filter and address:find(source_filter, 1, true) == 1 local specified = source_filter and address:find(source_filter, 1, true) == 1
if fs.list(install_path)() if
and (specified or fs.list(install_path)() and
not source_filter and (specified or
address ~= tmpAddress and not source_filter and address ~= tmpAddress and not (address == rootfs.address and not rootfs.isReadOnly()))
not (address == rootfs.address and not rootfs.isReadOnly())) then then
local prop = {} local prop = {}
local prop_path = path .. "/.prop" local prop_path = path .. "/.prop"
local prop_file = fs.open(prop_path) local prop_file = fs.open(prop_path)
@ -125,7 +127,7 @@ for dev, path in pairs(devices) do
end end
if not prop.ignore then if not prop.ignore then
if not label or label:lower() == (prop.label or dev.getLabel() or ""):lower() then if not label or label:lower() == (prop.label or dev.getLabel() or ""):lower() then
table.insert(sources, {dev=dev, path=install_path, prop=prop, specified=specified}) table.insert(sources, {dev = dev, path = install_path, prop = prop, specified = specified})
end end
end end
end end
@ -137,23 +139,24 @@ if #sources ~= 1 then
utils = loadfile(utils_path, "bt", _G) utils = loadfile(utils_path, "bt", _G)
source = utils("select", "sources", options, sources) source = utils("select", "sources", options, sources)
end end
if not source then return end if not source then
return
end
options = options = {
{ from = source.path .. "/",
from = source.path .. '/', fromDir = fs.canonical(options.fromDir or source.prop.fromDir or ""),
fromDir = fs.canonical(options.fromDir or source.prop.fromDir or ""), root = fs.canonical(options.root or options.toDir or source.prop.root or ""),
root = fs.canonical(options.root or options.toDir or source.prop.root or ""), update = options.update or options.u,
update = options.update or options.u, label = source.prop.label or label,
label = source.prop.label or label,
setlabel = not (options.nosetlabel or options.nolabelset) and source.prop.setlabel, setlabel = not (options.nosetlabel or options.nolabelset) and source.prop.setlabel,
setboot = not (options.nosetboot or options.noboot) and source.prop.setboot, setboot = not (options.nosetboot or options.noboot) and source.prop.setboot,
reboot = not options.noreboot and source.prop.reboot, reboot = not options.noreboot and source.prop.reboot
} }
local source_display = options.label or source.dev.getLabel() or source.path local source_display = options.label or source.dev.getLabel() or source.path
-- Remove the source from the target options -- Remove the source from the target options
for index,entry in ipairs(targets) do for index, entry in ipairs(targets) do
if entry.dev == source.dev then if entry.dev == source.dev then
table.remove(targets, index) table.remove(targets, index)
target = targets[1] target = targets[1]
@ -162,47 +165,67 @@ end
-- Ask the user to select a target -- Ask the user to select a target
if #targets ~= 1 then if #targets ~= 1 then
if #sources == 1 then if #sources == 1 then
io.write(source_display, " selected for install\n") io.write(source_display, " selected for install\n")
end end
utils = utils or loadfile(utils_path, "bt", _G) utils = utils or loadfile(utils_path, "bt", _G)
target = utils("select", "targets", options, targets) target = utils("select", "targets", options, targets)
end end
if not target then return end if not target then
return
end
options.to = target.path .. '/' options.to = target.path .. "/"
local cp_args = local function resolveFrom(path)
{ return fs.concat(options.from, options.fromDir) .. "/" .. path
"-vrx" .. (options.update and "ui" or ""), end
"--skip=.prop",
fs.concat(options.from, options.fromDir) .. "/.", local fullTargetPath = fs.concat(options.to, options.root)
fs.concat(options.to , options.root) local transfer_args = {
{
{resolveFrom("."), fullTargetPath},
{
cmd = "cp",
r = true, v = true, x = true, u = options.update, i = options.update,
skip = {resolveFrom(".prop")},
}
}
} }
if source.prop.noclobber and #source.prop.noclobber > 0 then
local noclobber_opts = {cmd = "cp", v = true, n = true}
for _, noclobber in ipairs(source.prop.noclobber or {}) do
local noclobberFrom = resolveFrom(noclobber)
local noclobberTo = fs.concat(fullTargetPath, noclobber)
table.insert(transfer_args[1][2].skip, noclobberFrom)
table.insert(transfer_args, {{noclobberFrom, noclobberTo}, noclobber_opts})
end
end
local special_target = "" local special_target = ""
if #targets > 1 or target_filter or source_filter then if #targets > 1 or target_filter or source_filter then
special_target = " to " .. cp_args[4] special_target = " to " .. transfer_args[1].args[2]
end end
io.write("Install " .. source_display .. special_target .. "? [Y/n] ") io.write("Install " .. source_display .. special_target .. "? [Y/n] ")
if not ((io.read() or "n").."y"):match("^%s*[Yy]") then if not ((io.read() or "n") .. "y"):match("^%s*[Yy]") then
io.write("Installation cancelled\n") io.write("Installation cancelled\n")
os.exit() os.exit()
end end
local installer_path = options.from .. "/.install" local installer_path = options.from .. "/.install"
if fs.exists(installer_path) then if fs.exists(installer_path) then
local installer, reason = loadfile(installer_path, "bt", setmetatable({install=options}, {__index = _G})) local installer, reason = loadfile(installer_path, "bt", setmetatable({install = options}, {__index = _G}))
if not installer then if not installer then
io.stderr:write("installer failed to load: " .. tostring(reason) .. '\n') io.stderr:write("installer failed to load: " .. tostring(reason) .. "\n")
os.exit(1) os.exit(1)
end end
os.exit(installer()) os.exit(installer())
end end
options.cp_args = cp_args options.cp_args = transfer_args
options.target = target options.target = target
return options return options

View File

@ -93,7 +93,7 @@ function lib.recurse(fromPath, toPath, options, origin, top)
local toPathFull = shell.resolve(toPath) local toPathFull = shell.resolve(toPath)
local mv = options.cmd == "mv" local mv = options.cmd == "mv"
local verbose = options.v and (not mv or top) local verbose = options.v and (not mv or top)
if select(2, fromPathFull:find(options.skip)) == #fromPathFull then if options.skip[fromPathFull] then
status(verbose, string.format("skipping %s", fromPath)) status(verbose, string.format("skipping %s", fromPath))
return true return true
end end
@ -221,8 +221,13 @@ function lib.batch(args, options)
-- standardized options -- standardized options
options.i = options.i and not options.f options.i = options.i and not options.f
options.P = options.P or options.r options.P = options.P or options.r
options.skip = text.escapeMagic(options.skip or "")
local skips = options.skip or {}
options.skip = {}
for _, skip_item in ipairs(skips) do
options.skip[shell.resolve(skip_item)] = true
end
local origin = {} local origin = {}
for dev,path in fs.mounts() do for dev,path in fs.mounts() do
origin[path] = dev origin[path] = dev