cc-libs/packages/trapos-core/apis/libinstallerdisk.lua
2026-06-16 22:16:58 +02:00

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;