mirror of
https://github.com/Metabolix/HackBGRT.git
synced 2025-12-07 09:36:10 -08:00
Add support for quiet setup
This commit is contained in:
25
README.md
25
README.md
@@ -23,21 +23,30 @@ When booting on a UEFI-based computer, Windows may show a vendor-defined logo wh
|
||||
* If you wish to change the image or other configuration, just reinstall.
|
||||
* For advanced settings, edit `config.txt` before installing. No extra support provided!
|
||||
|
||||
### Quiet (batch) installation
|
||||
|
||||
* Edit the `config.txt` and `splash.bmp` (or any other images) to your needs.
|
||||
* Run `setup.exe batch COMMANDS` as administrator, with some of the following commands:
|
||||
* `install` – copy the files but don't enable.
|
||||
* `enable-overwrite` – overwrite the MS boot loader.
|
||||
* `disable-overwrite` – restore the MS boot loader.
|
||||
* `allow-secure-boot` – ignore Secure Boot in subsequent commands.
|
||||
* `disable` – run all relevant `disable-*` commands.
|
||||
* `uninstall` – disable and remove completely.
|
||||
* For example, run `setup.exe batch install allow-secure-boot enable-overwrite` to copy files and overwrite the MS boot loader regardless of Secure Boot status.
|
||||
|
||||
### Multi-boot configurations
|
||||
|
||||
If you only need HackBGRT for Windows:
|
||||
|
||||
* Extract the latest release to `[EFI System Partition]\EFI\HackBGRT\`.
|
||||
* Set `boot=\EFI\Microsoft\Boot\bootmgfw.efi` in `config.txt`.
|
||||
* Point your boot loader to `\EFI\HackBGRT\bootx64.efi`.
|
||||
* Run `setup.exe`, install files without enabling.
|
||||
* Configure your boot loader to start `\EFI\HackBGRT\loader.efi`.
|
||||
|
||||
If you need it for other systems as well:
|
||||
|
||||
* Extract the latest release to `[EFI System Partition]\EFI\HackBGRT\`.
|
||||
* Set `boot=\EFI\your-actual-boot-loader.efi` in `config.txt`.
|
||||
* Set `\EFI\HackBGRT\bootx64.efi` as your default boot loader with `efibootmgr` or some other EFI boot manager tool.
|
||||
|
||||
On 32-bit machines, use `bootia32.efi` instead of `bootx64.efi`.
|
||||
* Configure HackBGRT to start your boot loader (such as systemd-boot): `boot=\EFI\systemd\systemd-bootx64.efi`.
|
||||
* Run `setup.exe`, install files.
|
||||
* Set `\EFI\HackBGRT\loader.efi` as your default boot loader with `efibootmgr` or some other EFI boot manager tool.
|
||||
|
||||
## Configuration
|
||||
|
||||
|
||||
198
src/Setup.cs
198
src/Setup.cs
@@ -3,11 +3,22 @@ using System.IO;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
|
||||
/**
|
||||
* HackBGRT Setup.
|
||||
*/
|
||||
public class Setup: SetupHelper {
|
||||
/** @var Version of the setup program. */
|
||||
const string Version =
|
||||
#if GIT_DESCRIBE
|
||||
GIT_DESCRIBE.data
|
||||
#else
|
||||
"unknown; not an official release?"
|
||||
#endif
|
||||
;
|
||||
|
||||
/**
|
||||
* The custom exception class for expected exceptions.
|
||||
*/
|
||||
@@ -26,6 +37,15 @@ public class Setup: SetupHelper {
|
||||
}
|
||||
}
|
||||
|
||||
/** @var The privileged actions. */
|
||||
protected static readonly string[] privilegedActions = new string[] {
|
||||
"install",
|
||||
"allow-secure-boot",
|
||||
"enable-overwrite", "disable-overwrite",
|
||||
"disable",
|
||||
"uninstall",
|
||||
};
|
||||
|
||||
/** @var The target directory. */
|
||||
protected string InstallPath;
|
||||
|
||||
@@ -36,11 +56,14 @@ public class Setup: SetupHelper {
|
||||
}
|
||||
}
|
||||
|
||||
/** @var Run in batch mode? */
|
||||
protected bool Batch;
|
||||
|
||||
/**
|
||||
* Find or mount or manually choose the EFI System Partition.
|
||||
*/
|
||||
public static void InitEspPath() {
|
||||
if (!Esp.Find() && !Esp.Mount()) {
|
||||
protected void InitEspPath() {
|
||||
if (Esp.Location == null && !Esp.Find() && !Esp.Mount() && !Batch) {
|
||||
Console.WriteLine("EFI System Partition was not found.");
|
||||
Console.WriteLine("Press enter to exit, or give ESP path here: ");
|
||||
string s = Console.ReadLine();
|
||||
@@ -165,7 +188,7 @@ public class Setup: SetupHelper {
|
||||
/**
|
||||
* Install files to ESP.
|
||||
*/
|
||||
protected void Install() {
|
||||
protected void InstallFiles() {
|
||||
try {
|
||||
if (!Directory.Exists(InstallPath)) {
|
||||
Directory.CreateDirectory(InstallPath);
|
||||
@@ -182,9 +205,6 @@ public class Setup: SetupHelper {
|
||||
// Duplicate check, but better to be sure...
|
||||
throw new SetupException("Failed to detect MS boot loader!");
|
||||
}
|
||||
if (!NewLoader.ReplaceWith(NewLoaderSource)) {
|
||||
throw new SetupException("Couldn't copy new HackBGRT to ESP.");
|
||||
}
|
||||
InstallFile("config.txt");
|
||||
foreach (var line in File.ReadAllLines("config.txt").Where(s => s.StartsWith("image="))) {
|
||||
var delim = "path=\\EFI\\HackBGRT\\";
|
||||
@@ -193,11 +213,32 @@ public class Setup: SetupHelper {
|
||||
InstallImageFile(line.Substring(i + delim.Length));
|
||||
}
|
||||
}
|
||||
InstallFile($"boot{EfiArch}.efi", "loader.efi");
|
||||
Console.WriteLine($"HackBGRT has been copied to {InstallPath}.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable HackBGRT by overwriting the MS boot loader.
|
||||
*/
|
||||
protected void OverwriteMsLoader() {
|
||||
var NewLoader = new BootLoaderInfo(Path.Combine(InstallPath, "loader.efi"));
|
||||
if (!MsLoader.ReplaceWith(NewLoader)) {
|
||||
MsLoader.ReplaceWith(MsLoaderBackup);
|
||||
throw new SetupException("Couldn't copy new HackBGRT over the MS loader (bootmgfw.efi).");
|
||||
}
|
||||
Console.WriteLine("HackBGRT is now installed.");
|
||||
Console.WriteLine($"Replaced {MsLoader.Path} with this app, stored backup in {MsLoaderBackup.Path}.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore the MS boot loader if it was previously replaced.
|
||||
*/
|
||||
protected void RestoreMsLoader() {
|
||||
if (MsLoader.Type != BootLoaderType.MS) {
|
||||
if (!MsLoader.ReplaceWith(MsLoaderBackup)) {
|
||||
throw new SetupException("Couldn't restore the old MS loader.");
|
||||
}
|
||||
Console.WriteLine($"{MsLoader.Path} has been restored.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -220,22 +261,11 @@ public class Setup: SetupHelper {
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable HackBGRT, restore the original boot loader.
|
||||
*/
|
||||
protected void Disable() {
|
||||
if (MsLoader.Type != BootLoaderType.MS) {
|
||||
if (!MsLoader.ReplaceWith(MsLoaderBackup)) {
|
||||
throw new SetupException("Couldn't restore the old MS loader.");
|
||||
}
|
||||
}
|
||||
Console.WriteLine(MsLoader.Path + " has been restored.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Uninstall HackBGRT completely.
|
||||
*/
|
||||
protected void Uninstall() {
|
||||
RestoreMsLoader();
|
||||
try {
|
||||
Directory.Delete(InstallPath, true);
|
||||
Console.WriteLine("HackBGRT has been removed.");
|
||||
@@ -246,8 +276,10 @@ public class Setup: SetupHelper {
|
||||
|
||||
/**
|
||||
* Check Secure Boot status and inform the user.
|
||||
*
|
||||
* @param allowSecureBoot Allow Secure Boot to be enabled?
|
||||
*/
|
||||
public static void HandleSecureBoot() {
|
||||
protected void HandleSecureBoot(bool allowSecureBoot) {
|
||||
int secureBoot = Efi.GetSecureBootStatus();
|
||||
if (secureBoot == 0) {
|
||||
Console.WriteLine("Secure Boot is disabled, good!");
|
||||
@@ -259,6 +291,12 @@ public class Setup: SetupHelper {
|
||||
}
|
||||
Console.WriteLine("It's very important to disable Secure Boot before installing.");
|
||||
Console.WriteLine("Otherwise your machine may become unbootable.");
|
||||
if (Batch) {
|
||||
if (allowSecureBoot) {
|
||||
return;
|
||||
}
|
||||
throw new SetupException("Aborting because of Secure Boot.");
|
||||
}
|
||||
Console.WriteLine("Choose action (press a key):");
|
||||
bool canBootToFW = Efi.CanBootToFW();
|
||||
if (canBootToFW) {
|
||||
@@ -281,22 +319,20 @@ public class Setup: SetupHelper {
|
||||
if (k2 == ConsoleKey.R) {
|
||||
StartProcess("shutdown", "-f -r -t 1");
|
||||
}
|
||||
Environment.Exit(0);
|
||||
throw new ExitSetup(0);
|
||||
} else {
|
||||
Console.WriteLine("Aborting because of Secure Boot.");
|
||||
throw new ExitSetup(1);
|
||||
throw new SetupException("Aborting because of Secure Boot.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected BootLoaderInfo MsLoader, MsLoaderBackup, NewLoader, NewLoaderSource;
|
||||
protected BootLoaderInfo MsLoader, MsLoaderBackup;
|
||||
protected string EfiArch;
|
||||
|
||||
/**
|
||||
* Initialize information for the Setup.
|
||||
*/
|
||||
protected Setup() {
|
||||
protected void InitEspInfo() {
|
||||
InstallPath = Path.Combine(Esp.Location, "EFI", "HackBGRT");
|
||||
MsLoaderBackup = new BootLoaderInfo(BackupLoaderPath);
|
||||
MsLoader = new BootLoaderInfo(Esp.MsLoaderPath);
|
||||
@@ -307,18 +343,13 @@ public class Setup: SetupHelper {
|
||||
} else {
|
||||
throw new SetupException("Failed to detect MS boot loader!");
|
||||
}
|
||||
string loaderName = "boot" + EfiArch + ".efi";
|
||||
NewLoaderSource = new BootLoaderInfo(loaderName);
|
||||
NewLoader = new BootLoaderInfo(Path.Combine(InstallPath, loaderName));
|
||||
if (!NewLoaderSource.Exists) {
|
||||
throw new SetupException("Couldn't find required files! Missing: " + NewLoaderSource.Path);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ask for user's choice and install/uninstall.
|
||||
*/
|
||||
protected void HandleUserAction() {
|
||||
protected void ShowMenu() {
|
||||
var NewLoader = new BootLoaderInfo(Path.Combine(InstallPath, "loader.efi"));
|
||||
bool isEnabled = MsLoader.Type == BootLoaderType.Own;
|
||||
bool isInstalled = NewLoader.Type == BootLoaderType.Own;
|
||||
|
||||
@@ -334,6 +365,7 @@ public class Setup: SetupHelper {
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Choose action (press a key):");
|
||||
Console.WriteLine(" I = install, upgrade, repair, modify...");
|
||||
Console.WriteLine(" F = install files only, don't enable");
|
||||
if (isEnabled) {
|
||||
Console.WriteLine(" D = disable, restore the original boot loader");
|
||||
}
|
||||
@@ -344,16 +376,17 @@ public class Setup: SetupHelper {
|
||||
|
||||
var k = Console.ReadKey().Key;
|
||||
Console.WriteLine();
|
||||
if (k == ConsoleKey.I) {
|
||||
if (k == ConsoleKey.I || k == ConsoleKey.F) {
|
||||
Configure();
|
||||
Install();
|
||||
} else if ((isEnabled && k == ConsoleKey.D) || (isInstalled && k == ConsoleKey.R)) {
|
||||
if (isEnabled) {
|
||||
Disable();
|
||||
}
|
||||
if (k == ConsoleKey.R) {
|
||||
Uninstall(); // NOTICE: It's imperative to Disable() first!
|
||||
}
|
||||
}
|
||||
if (k == ConsoleKey.I) {
|
||||
RunPrivilegedActions(new string[] { "install", "enable-overwrite" });
|
||||
} else if (k == ConsoleKey.F) {
|
||||
RunPrivilegedActions(new string[] { "install" });
|
||||
} else if (k == ConsoleKey.D) {
|
||||
RunPrivilegedActions(new string[] { "disable" });
|
||||
} else if (k == ConsoleKey.R) {
|
||||
RunPrivilegedActions(new string[] { "uninstall" });
|
||||
} else if (k == ConsoleKey.C) {
|
||||
throw new ExitSetup(1);
|
||||
} else {
|
||||
@@ -362,25 +395,88 @@ public class Setup: SetupHelper {
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the setup.
|
||||
* Run privileged actions.
|
||||
*
|
||||
* @param actions The actions to run.
|
||||
*/
|
||||
public static void RunSetup() {
|
||||
protected void RunPrivilegedActions(IEnumerable<string> actions) {
|
||||
bool allowSecureBoot = false;
|
||||
foreach (var arg in actions) {
|
||||
Log($"Running action '{arg}'.");
|
||||
if (arg == "install") {
|
||||
InstallFiles();
|
||||
} else if (arg == "allow-secure-boot") {
|
||||
allowSecureBoot = true;
|
||||
} else if (arg == "enable-overwrite") {
|
||||
HandleSecureBoot(allowSecureBoot);
|
||||
OverwriteMsLoader();
|
||||
} else if (arg == "disable-overwrite") {
|
||||
RestoreMsLoader();
|
||||
} else if (arg == "disable") {
|
||||
RestoreMsLoader();
|
||||
} else if (arg == "uninstall") {
|
||||
Uninstall();
|
||||
} else {
|
||||
throw new SetupException($"Invalid action: '{arg}'!");
|
||||
}
|
||||
Console.WriteLine($"Completed action '{arg}' successfully.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The Main program.
|
||||
*
|
||||
* @param args The arguments.
|
||||
*/
|
||||
public static void Main(string[] args) {
|
||||
Console.WriteLine($"HackBGRT installer version: {Version}");
|
||||
var self = Assembly.GetExecutingAssembly().Location;
|
||||
Directory.SetCurrentDirectory(Path.GetDirectoryName(self));
|
||||
Environment.ExitCode = new Setup().Run(args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the setup.
|
||||
*
|
||||
* @param args The arguments.
|
||||
*/
|
||||
protected int Run(string[] args) {
|
||||
Batch = args.Contains("batch");
|
||||
try {
|
||||
if (args.Contains("is-elevated") && !HasPrivileges()) {
|
||||
Console.WriteLine("This installer needs to be run as administrator!");
|
||||
return 1;
|
||||
}
|
||||
if (!HasPrivileges()) {
|
||||
var self = Assembly.GetExecutingAssembly().Location;
|
||||
return RunElevated(self, String.Join(" ", args.Prepend("is-elevated")));
|
||||
}
|
||||
InitEspPath();
|
||||
HandleSecureBoot();
|
||||
var s = new Setup();
|
||||
s.HandleUserAction();
|
||||
InitEspInfo();
|
||||
var actions = args.Where(s => privilegedActions.Contains(s));
|
||||
if (actions.Count() > 0) {
|
||||
RunPrivilegedActions(actions);
|
||||
return 0;
|
||||
}
|
||||
if (Batch) {
|
||||
throw new SetupException("No action specified!");
|
||||
}
|
||||
ShowMenu();
|
||||
return 0;
|
||||
} catch (ExitSetup e) {
|
||||
Environment.ExitCode = e.Code;
|
||||
return e.Code;
|
||||
} catch (SetupException e) {
|
||||
Console.WriteLine("Error: {0}", e.Message);
|
||||
Environment.ExitCode = 1;
|
||||
return 1;
|
||||
} catch (Exception e) {
|
||||
Console.WriteLine("Unexpected error!\n{0}", e.ToString());
|
||||
Console.WriteLine("If this is the most current release, please report this bug.");
|
||||
Environment.ExitCode = 1;
|
||||
return 1;
|
||||
} finally {
|
||||
if (!Batch) {
|
||||
Console.WriteLine("Press any key to quit.");
|
||||
Console.ReadKey();
|
||||
}
|
||||
}
|
||||
Console.WriteLine("Press any key to quit.");
|
||||
Console.ReadKey();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Security.Principal;
|
||||
using System.Linq;
|
||||
|
||||
/**
|
||||
* Helper functions.
|
||||
@@ -65,41 +63,39 @@ public class SetupHelper {
|
||||
}
|
||||
|
||||
/**
|
||||
* The Main program.
|
||||
* Check for required privileges (access to ESP and EFI vars).
|
||||
*
|
||||
* @param args The arguments.
|
||||
* @return True, if the file was successfully copied.
|
||||
*/
|
||||
public static void Main(string[] args) {
|
||||
#if GIT_DESCRIBE
|
||||
Console.WriteLine("HackBGRT installer version: {0}", GIT_DESCRIBE.data);
|
||||
#else
|
||||
Console.WriteLine("HackBGRT installer version: unknown; not an official release?");
|
||||
#endif
|
||||
var self = Assembly.GetExecutingAssembly().Location;
|
||||
public static bool HasPrivileges() {
|
||||
try {
|
||||
// Relaunch as admin, if needed.
|
||||
var id = WindowsIdentity.GetCurrent();
|
||||
var principal = new WindowsPrincipal(id);
|
||||
var admin = WindowsBuiltInRole.Administrator;
|
||||
if (!principal.IsInRole(admin) && !args.Contains("no-elevate")) {
|
||||
ProcessStartInfo startInfo = new ProcessStartInfo(self);
|
||||
startInfo.Arguments = "no-elevate";
|
||||
startInfo.Verb = "runas";
|
||||
startInfo.UseShellExecute = true;
|
||||
Process p = Process.Start(startInfo);
|
||||
p.WaitForExit();
|
||||
Environment.ExitCode = p.ExitCode;
|
||||
return;
|
||||
// FIXME: Check access to ESP as well.
|
||||
if (!principal.IsInRole(admin)) {
|
||||
return false;
|
||||
}
|
||||
Efi.EnablePrivilege();
|
||||
return true;
|
||||
} catch {
|
||||
Console.WriteLine("This installer needs to be run as administrator!");
|
||||
Console.WriteLine("Press any key to quit.");
|
||||
Console.ReadKey();
|
||||
Environment.ExitCode = 1;
|
||||
return;
|
||||
}
|
||||
Directory.SetCurrentDirectory(Path.GetDirectoryName(self));
|
||||
Setup.RunSetup();
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run another process as admin, return the exit code.
|
||||
*
|
||||
* @param app Path to the application.
|
||||
* @param args The argument string.
|
||||
*/
|
||||
public static int RunElevated(string app, string args) {
|
||||
ProcessStartInfo startInfo = new ProcessStartInfo(app);
|
||||
startInfo.Arguments = args;
|
||||
startInfo.Verb = "runas";
|
||||
startInfo.UseShellExecute = true;
|
||||
Process p = Process.Start(startInfo);
|
||||
p.WaitForExit();
|
||||
return p.ExitCode;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user