import Gio from "gi://Gio"; import { ExecCommand, Log, LogWarning } from './utils.js'; /** * Represents Systemdboot */ export class SystemdBoot { /** * Get's all available boot options * @returns {[Map, string]} Map(title, id), defaultOption */ static async GetBootOptions() { let bootctl = await this.GetBinary(); if (bootctl == "") { Log(`Failed to find bootctl binary`); return undefined; } try { let [status, stdout, stderr] = await ExecCommand([bootctl, "list"]); if (status !== 0) throw new Error(`Failed to get list from bootctl: ${status}\n${stdout}\n${stderr}`); Log(`bootctl list: ${status}\n${stdout}\n${stderr}`); let lines = String(stdout).split('\n'); let titleRx = /(?<=title:\s+).+/; let idRx = /(?<=id:\s+).+/; let defaultRx = /\(default\)/; let titles = []; let ids = [] let defaultOpt; lines.forEach(l => { let title = titleRx.exec(l); let id = idRx.exec(l); if (title && title.length) { titles.push(String(title)); } else if (id && id.length) { ids.push(String(id)); } }); if (titles.length !== ids.length) throw new Error("Number of titles and ids do not match!"); let bootOptions = new Map(); for (let i = 0; i < titles.length; i++) { bootOptions.set(titles[i], ids[i]) } bootOptions.forEach((id, title) => { Log(`${id} = ${title}`); let defaultRes = defaultRx.exec(title); if (defaultRes) { defaultOpt = title; } }) return [bootOptions, bootOptions.get(defaultOpt)]; } catch (e) { LogWarning(e); return undefined; } } /** * Set's the next boot option * @param {string} id * @returns True if the boot option was set, otherwise false */ static async SetBootOption(id) { if (!this.IsUseable()) return false; const [status, stdout, stderr] = await ExecCommand(['/usr/bin/pkexec', '/usr/bin/bootctl', 'set-oneshot', id],); if (status === 0) { Log(`Set boot option to ${id}`); return true; } LogWarning("Unable to set boot option using bootctl"); return false; } /** * Can we use this bootloader? * @returns True if useable otherwise false */ static async IsUseable() { return await this.GetBinary() !== ""; } /** * Get's bootctl binary path * @returns A string containing the location of the binary, if none is found returns a blank string */ static async GetBinary() { let paths = ["/usr/sbin/bootctl", "/usr/bin/bootctl"]; let file; for (let i = 0; i < paths.length; i++) { file = Gio.file_new_for_path(paths[i]); if (file.query_exists(null)) { return paths[i]; } } return ""; } /** * This boot loader cannot be quick rebooted */ static async CanQuickReboot() { return false; } /** * This boot loader cannot be quick rebooted */ static async QuickRebootEnabled() { return false; } }