-
Notifications
You must be signed in to change notification settings - Fork 64
custom reboot #119
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
slaclau
wants to merge
6
commits into
master
Choose a base branch
from
custom-reboot
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
custom reboot #119
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
f0e6e64
Add customreboot functionality
slaclau 45e962e
Add preferences for customreboot
slaclau b76f456
Add scrolling functionality to custom reboot menu
slaclau 55e7caa
Fix import issues in prefs, unable to test boot manager options now t…
slaclau b22c714
Repair issues with efibootmgr, response was being parsed incorrectly
slaclau 76f8260
Remove unneeded header
slaclau File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| #! /bin/sh | ||
| # This script allows the extension to skip grub's timeout when rebooting | ||
|
|
||
| set -e | ||
|
|
||
| cat << EOF | ||
| if [ ! -z "\${boot_once}" ] ; then | ||
| set timeout_style=hidden | ||
| set timeout=0 | ||
| fi | ||
| EOF |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| import { Extension } from 'resource:///org/gnome/shell/extensions/extension.js'; | ||
| import { EFIBootManager } from "./efibootmgr.js"; | ||
| import { SystemdBoot } from './systemdBoot.js'; | ||
| import { Grub } from './grub.js'; | ||
|
|
||
| import * as Utils from "./utils.js"; | ||
| import GLib from 'gi://GLib'; | ||
| import Gio from "gi://Gio"; | ||
|
|
||
| export const BootLoaders = { | ||
| EFI: "EFI Boot Manager", | ||
| GRUB: "Grub", | ||
| SYSD: "Systemd Boot", | ||
| UNKNOWN: "Unknown Boot Loader" | ||
| } | ||
|
|
||
| export class Bootloader { | ||
| /** | ||
| * Gets the first available boot loader type on the current system | ||
| * @returns BootLoaders type. Can be "EFI", "SYSD", "GRUB", or "UNKNOWN" | ||
| */ | ||
| static async GetUseableType() { | ||
| const settings = Extension.lookupByUUID('hibernate-status@dromi').getSettings('org.gnome.shell.extensions.hibernate-status-button'); | ||
|
|
||
| if (await EFIBootManager.IsUseable() && settings.get_boolean('use-efibootmgr')) return BootLoaders.EFI; | ||
| if (await Grub.IsUseable() && settings.get_boolean('use-grub')) return BootLoaders.GRUB; | ||
| if (await SystemdBoot.IsUseable() && settings.get_boolean('use-systemd-boot')) return BootLoaders.SYSD; | ||
| return BootLoaders.UNKNOWN; | ||
| } | ||
|
|
||
| /** | ||
| * Gets a instance of the provided boot loader | ||
| * @returns A boot loader if one is found otherwise undefined | ||
| */ | ||
| static async GetUseable(type) { | ||
| if (type === BootLoaders.EFI) return EFIBootManager; | ||
| if (type === BootLoaders.SYSD) return SystemdBoot; | ||
| if (type === BootLoaders.GRUB) return Grub; | ||
| return undefined; | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,102 @@ | ||
| import Gio from "gi://Gio"; | ||
| import { ExecCommand, Log, LogWarning } from './utils.js'; | ||
|
|
||
| /** | ||
| * Represents efibootmgr | ||
| */ | ||
| export class EFIBootManager { | ||
| /** | ||
| * Get's all available boot options | ||
| * @returns {[Map, string]} Map(title, id), defaultOption | ||
| */ | ||
| static async GetBootOptions() { | ||
| const [status, stdout, stderr] = await ExecCommand(['efibootmgr'],); | ||
| const lines = stdout.split("\n"); | ||
|
|
||
| let boot_first = "0000"; | ||
|
|
||
| const boot_options = new Map(); | ||
|
|
||
| for (let l = 0; l < lines.length; l++) { | ||
| const line = lines[l]; | ||
| if (line.startsWith("BootOrder:")) { | ||
| boot_first = line.split(" ")[1].split(",")[0]; | ||
| continue; | ||
| } | ||
|
|
||
| const regex = /(Boot[0-9]{4})/; | ||
| const vLine = regex.exec(line) | ||
| if (vLine && vLine.length) { | ||
| const option = line.replace("Boot", "").replace("*"," ").split(" "); | ||
| var title = option[1]; | ||
| //title = title.split("\t")[0] | ||
| if (title.includes("HD") || title.includes("RC") || title.includes("PciRoot")) { | ||
| const trimed_title = title.replace(/(?<=[\S\s]*)(HD|RC|PciRoot)([\s\S()]*|$)/, "").trim(); | ||
| boot_options.set(trimed_title, option[0].trim()); | ||
| } | ||
| else { | ||
| boot_options.set(option[1].trim(), option[0].trim()); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return [boot_options, boot_first]; | ||
| } | ||
|
|
||
| /** | ||
| * 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', 'efibootmgr', '-n', id],); | ||
| if (status === 0) { | ||
| Log(`Set boot option to ${id}`); | ||
| return true; | ||
| } | ||
| LogWarning("Unable to set boot option using efibootmgr"); | ||
| return false; | ||
| } | ||
|
|
||
| /** | ||
| * Can we use this bootloader? | ||
| * @returns True if useable otherwise false | ||
| */ | ||
| static async IsUseable() { | ||
| return await this.GetBinary() !== ""; | ||
| } | ||
|
|
||
| /** | ||
| * Get's efibootmgr 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/bin/efibootmgr"]; | ||
|
|
||
| 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; | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,205 @@ | ||
| import Gio from "gi://Gio"; | ||
| import { ExecCommand, Log, LogWarning } from './utils.js'; | ||
| import { Extension } from "resource:///org/gnome/shell/extensions/extension.js" | ||
| /** | ||
| * Represents grub | ||
| */ | ||
| export class Grub { | ||
| /** | ||
| * Get's all available boot options | ||
| * @returns {[Map, string]} Map(title, id), defaultOption | ||
| */ | ||
| static async GetBootOptions() { | ||
| try { | ||
| let cfgpath = await this.GetConfig(); | ||
| if (cfgpath == "") { | ||
| throw new String("Failed to find grub config"); | ||
| } | ||
|
|
||
| let bootOptions = new Map(); | ||
|
|
||
| let defualtEn = ""; | ||
|
|
||
| let file = Gio.file_new_for_path(cfgpath); | ||
| let [suc, content] = file.load_contents(null); | ||
| if (!suc) { | ||
| throw new String("Failed to load grub config"); | ||
| } | ||
|
|
||
| let lines; | ||
| if (content instanceof Uint8Array) { | ||
| lines = new TextDecoder().decode(content); | ||
| } | ||
| else { | ||
| lines = content.toString(); | ||
| } | ||
|
|
||
| let entryRx = /^menuentry ['"]([^'"]+)/; | ||
| let defaultRx = /(?<=set default=\")([A-Za-z- ()/0-9]*)(?=\")/ | ||
| lines.split('\n').forEach(l => { | ||
| let res = entryRx.exec(l); | ||
| if (res && res.length) { | ||
| bootOptions.set(res[1], res[1]); | ||
| } | ||
| let def = defaultRx.exec(l); | ||
| if (def && def.length) { | ||
| defualtEn = def[0]; | ||
| } | ||
| }); | ||
|
|
||
| bootOptions.forEach((v, k) => { | ||
| Log(`${k} = ${v}`); | ||
| }); | ||
|
|
||
| if (defualtEn == "") defualtEn = bootOptions.keys().next().value; | ||
|
|
||
| return [bootOptions, defualtEn]; | ||
|
|
||
| } 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) { | ||
| try { | ||
| let [status, stdout, stderr] = await ExecCommand( | ||
| ['/usr/bin/pkexec', '/usr/sbin/grub-reboot', id], | ||
| ); | ||
| Log(`Set boot option to ${id}: ${status}\n${stdout}\n${stderr}`); | ||
| return true; | ||
| } catch (e) { | ||
| LogWarning(e); | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Can we use this bootloader? | ||
| * @returns True if useable otherwise false | ||
| */ | ||
| static async IsUseable() { | ||
| return await this.GetConfig() !== ""; | ||
| } | ||
|
|
||
| /** | ||
| * Get's grub config file | ||
| * @returns A string containing the location of the config file, if none is found returns a blank string | ||
| */ | ||
| static async GetConfig() { | ||
| let paths = ["/boot/grub/grub.cfg", "/boot/grub2/grub.cfg"]; | ||
|
|
||
| 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 ""; | ||
| } | ||
|
|
||
| /** | ||
| * Copies a custom grub script to allow the extension to quickly reboot into another OS | ||
| * If anyone reads this: Idk how to combine these into one pkexec call, if you do please leave a commit fixing it | ||
| */ | ||
| static async EnableQuickReboot(ext) { | ||
| try { | ||
| let [status, stdout, stderr] = await ExecCommand([ | ||
| 'pkexec', | ||
| 'sh', | ||
| '-c', | ||
| `/usr/bin/cp ${Extension.lookupByUUID('hibernate-status@dromi').path}/customreboot/42_custom_reboot /etc/grub.d/42_custom_reboot && /usr/bin/chmod 755 /etc/grub.d/42_custom_reboot && /usr/sbin/update-grub` | ||
| ]); | ||
|
|
||
| if (status !== 0) { | ||
| return false; | ||
| } | ||
|
|
||
| return true; | ||
| } | ||
| catch (e) { | ||
| LogWarning(e); | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
|
|
||
| /** | ||
| * Removes the script used to allow the extension to quickly reboot into another OS without waiting for grub's timeout | ||
| * If anyone reads this: Idk how to combine these into one pkexec call, if you do please leave a commit fixing it | ||
| */ | ||
| static async DisableQuickReboot() { | ||
| try { | ||
|
|
||
| let [status, stdout, stderr] = await ExecCommand([ | ||
| 'pkexec', | ||
| 'sh', | ||
| '-c', | ||
| '/usr/bin/rm /etc/grub.d/42_custom_reboot && /usr/sbin/update-grub' | ||
| ]); | ||
|
|
||
| if (status !== 0) { | ||
| return false; | ||
| } | ||
|
|
||
| return true; | ||
| } | ||
| catch (e) { | ||
| LogWarning(e); | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
|
|
||
| /** | ||
| * This boot loader can be quick rebooted | ||
| */ | ||
| static async CanQuickReboot() { | ||
| return true; | ||
| } | ||
|
|
||
| /** | ||
| * Checks if /etc/grub.d/42_custom_reboot exists | ||
| */ | ||
| static async QuickRebootEnabled() { | ||
| try { | ||
| let [status, stdout, stderr] = await ExecCommand(['/usr/bin/cat', '/etc/grub.d/42_custom_reboot'],); | ||
| if (status !== 0) { | ||
| LogWarning(`/etc/grub.d/42_custom_reboot not found`); | ||
| return false; | ||
| } | ||
| Log(`/etc/grub.d/42_custom_reboot found`); | ||
|
|
||
| return true; | ||
| } | ||
| catch (e) { | ||
| LogWarning(e); | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| static async SetReadable() { | ||
| try { | ||
| const config = GetConfig(); | ||
| let [status, stdout, stderr] = await ExecCommand(['/usr/bin/pkexec', '/usr/bin/chmod', '644', config],); | ||
| if (status !== 0) { | ||
| Log(`Failed to make ${config} readable`); | ||
| return false; | ||
| } | ||
| Log(`Made ${config} readable`); | ||
| return true; | ||
| } | ||
| catch (e) { | ||
| Log(e); | ||
| return false; | ||
| } | ||
| } | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
update-grubcommand has been replaced bygrub-mkconfig: https://git.savannah.gnu.org/cgit/grub.git/tree/NEWS#n537There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll change this, thanks.