220 lines
6.9 KiB
Lua
220 lines
6.9 KiB
Lua
local DEFAULT_DISK_LABEL = 'TrapOS Disk';
|
|
local DEFAULT_INSTALL_URL = 'https://os.trapcloud.fr/install';
|
|
local DEFAULT_SETTINGS_SOURCE_PATH = '/.settings';
|
|
|
|
local function luaQuote(value)
|
|
return string.format('%q', tostring(value));
|
|
end
|
|
|
|
local function isValidMode(mode)
|
|
return mode == 'install' or mode == 'reinstall' or mode == 'uninstall';
|
|
end
|
|
|
|
local function writeFile(path, body)
|
|
local file = fs.open(path, 'w');
|
|
if not file then return false; end
|
|
file.write(body);
|
|
file.close();
|
|
return true;
|
|
end
|
|
|
|
local function createInstallerDisk(opts)
|
|
opts = opts or {};
|
|
local peripheralApi = opts.peripheral or peripheral;
|
|
local settingsApi = opts.settings or settings;
|
|
local settingsSourcePath = opts.settingsSourcePath or DEFAULT_SETTINGS_SOURCE_PATH;
|
|
local diskLabel = opts.diskLabel or opts.installerLabel or DEFAULT_DISK_LABEL;
|
|
local installUrl = opts.installUrl or DEFAULT_INSTALL_URL;
|
|
|
|
local api = {};
|
|
|
|
local function buildHeader(mode)
|
|
return '-- TrapOS ' .. mode .. ' disk autorun (generated; do not edit)\n'
|
|
.. 'local DISK_LABEL = ' .. luaQuote(diskLabel) .. ';\n'
|
|
.. 'local INSTALL_URL = ' .. luaQuote(installUrl) .. ';\n'
|
|
.. '\n';
|
|
end
|
|
|
|
local function buildDiskMountFunction()
|
|
return 'local function diskMount()\n'
|
|
.. ' for _, name in ipairs(peripheral.getNames()) do\n'
|
|
.. " if peripheral.getType(name) == 'drive' then\n"
|
|
.. ' local drive = peripheral.wrap(name);\n'
|
|
.. ' if drive.isDiskPresent() and drive.getDiskLabel() == DISK_LABEL then\n'
|
|
.. ' return drive.getMountPath();\n'
|
|
.. ' end\n'
|
|
.. ' end\n'
|
|
.. ' end\n'
|
|
.. 'end\n'
|
|
.. '\n';
|
|
end
|
|
|
|
local function buildWriteMarkerFunction()
|
|
return 'local function writeMarker(path)\n'
|
|
.. " local file = fs.open(path, 'w');\n"
|
|
.. ' if not file then return false; end\n'
|
|
.. " file.write('1');\n"
|
|
.. ' file.close();\n'
|
|
.. ' return true;\n'
|
|
.. 'end\n'
|
|
.. '\n';
|
|
end
|
|
|
|
function api.buildAutorun(mode)
|
|
mode = mode or 'install';
|
|
if not isValidMode(mode) then
|
|
error('invalid disk mode: ' .. tostring(mode), 2);
|
|
end
|
|
|
|
if mode == 'install' then
|
|
return buildHeader(mode)
|
|
.. "if fs.exists('/trapos') then return; end\n"
|
|
.. '\n'
|
|
.. buildDiskMountFunction()
|
|
.. 'local mount = diskMount();\n'
|
|
.. 'if mount then\n'
|
|
.. " local src = fs.combine(mount, '.settings');\n"
|
|
.. ' if fs.exists(src) then\n'
|
|
.. " fs.delete('/.settings');\n"
|
|
.. " fs.copy(src, '/.settings');\n"
|
|
.. ' settings.load();\n'
|
|
.. ' end\n'
|
|
.. 'end\n'
|
|
.. '\n'
|
|
.. "print('TrapOS Disk: installing TrapOS...');\n"
|
|
.. "if not shell.run('wget', 'run', INSTALL_URL) then\n"
|
|
.. " print('TrapOS install failed.');\n"
|
|
.. 'end\n';
|
|
end
|
|
|
|
if mode == 'uninstall' then
|
|
return buildHeader(mode)
|
|
.. "if not fs.exists('/trapos') then\n"
|
|
.. " print('TrapOS already removed.');\n"
|
|
.. 'else\n'
|
|
.. " print('TrapOS Disk: uninstalling TrapOS...');\n"
|
|
.. " if not shell.run('ccpm', 'uninstall', 'trapos', '--yes') then\n"
|
|
.. " print('TrapOS uninstall failed.');\n"
|
|
.. ' return;\n'
|
|
.. ' end\n'
|
|
.. 'end\n'
|
|
.. '\n'
|
|
.. "print('TrapOS removed. Eject the disk to finish.');\n"
|
|
.. "os.pullEvent('disk_eject');\n"
|
|
.. 'os.reboot();\n';
|
|
end
|
|
|
|
return buildHeader(mode)
|
|
.. buildDiskMountFunction()
|
|
.. buildWriteMarkerFunction()
|
|
.. 'local mount = diskMount();\n'
|
|
.. 'if not mount then\n'
|
|
.. " print('TrapOS Disk not found.');\n"
|
|
.. ' return;\n'
|
|
.. 'end\n'
|
|
.. "local MARKER = fs.combine(mount, '.trapos-reinstalled');\n"
|
|
.. "if fs.exists(MARKER) and fs.exists('/trapos') then\n"
|
|
.. " print('Reinstall complete. Eject the disk.');\n"
|
|
.. " os.pullEvent('disk_eject');\n"
|
|
.. ' os.reboot();\n'
|
|
.. ' return;\n'
|
|
.. 'elseif fs.exists(MARKER) then\n'
|
|
.. ' fs.delete(MARKER);\n'
|
|
.. 'end\n'
|
|
.. "if fs.exists('/trapos') then\n"
|
|
.. " print('TrapOS Disk: removing current TrapOS...');\n"
|
|
.. " if not shell.run('ccpm', 'uninstall', 'trapos', '--yes') then\n"
|
|
.. " print('TrapOS uninstall failed.');\n"
|
|
.. ' return;\n'
|
|
.. ' end\n'
|
|
.. 'end\n'
|
|
.. 'if not writeMarker(MARKER) then\n'
|
|
.. " print('Failed to write reinstall marker.');\n"
|
|
.. ' return;\n'
|
|
.. 'end\n'
|
|
.. "print('TrapOS Disk: reinstalling TrapOS...');\n"
|
|
.. "if not shell.run('wget', 'run', INSTALL_URL) then\n"
|
|
.. ' fs.delete(MARKER);\n'
|
|
.. " print('TrapOS install failed.');\n"
|
|
.. ' return;\n'
|
|
.. 'end\n';
|
|
end
|
|
|
|
function api.findTargetDrive()
|
|
local names = peripheralApi.getNames and peripheralApi.getNames() or {};
|
|
local presentCount = 0;
|
|
local nonEmptyCount = 0;
|
|
local empty = {};
|
|
|
|
for _, name in ipairs(names) do
|
|
if peripheralApi.getType(name) == 'drive' then
|
|
local drive = peripheralApi.wrap(name);
|
|
if drive.isDiskPresent() and drive.hasData() then
|
|
presentCount = presentCount + 1;
|
|
local mountPath = drive.getMountPath();
|
|
if mountPath and #fs.list(mountPath) == 0 then
|
|
empty[#empty + 1] = { name = name, mountPath = mountPath, drive = drive };
|
|
else
|
|
nonEmptyCount = nonEmptyCount + 1;
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
if #empty > 1 then
|
|
return false, 'multiple blank disks found, remove all but one';
|
|
end
|
|
if #empty == 1 then
|
|
return true, empty[1];
|
|
end
|
|
if presentCount > 0 or nonEmptyCount > 0 then
|
|
return false, 'disk not empty; insert a blank floppy';
|
|
end
|
|
return false, 'insert a blank floppy';
|
|
end
|
|
|
|
function api.create(createOpts)
|
|
createOpts = createOpts or {};
|
|
local mode = createOpts.mode or 'install';
|
|
if not isValidMode(mode) then
|
|
return false, 'invalid disk mode: ' .. tostring(mode);
|
|
end
|
|
|
|
local ok, target = api.findTargetDrive();
|
|
if not ok then return false, target; end
|
|
|
|
target.drive.setDiskLabel(diskLabel);
|
|
|
|
local result = {
|
|
name = target.name,
|
|
mountPath = target.mountPath,
|
|
label = diskLabel,
|
|
mode = mode,
|
|
settingsCopied = false,
|
|
settingsWarning = false,
|
|
};
|
|
|
|
if mode == 'install' and fs.exists(settingsSourcePath) then
|
|
settingsApi.save();
|
|
local dest = fs.combine(target.mountPath, '.settings');
|
|
fs.delete(dest);
|
|
fs.copy(settingsSourcePath, dest);
|
|
result.settingsCopied = true;
|
|
elseif mode == 'install' then
|
|
result.settingsWarning = true;
|
|
end
|
|
|
|
local startupPath = fs.combine(target.mountPath, 'startup.lua');
|
|
fs.delete(startupPath);
|
|
if not writeFile(startupPath, api.buildAutorun(mode)) then
|
|
return false, 'failed to write ' .. startupPath;
|
|
end
|
|
|
|
return true, result;
|
|
end
|
|
|
|
return api;
|
|
end
|
|
|
|
return createInstallerDisk;
|