/* * Copyright 2008-2011, Haiku, Inc. All rights reserved. * Distributed under the terms of the MIT License. * * Authors: * Axel Dörfler, axeld@pinc-software.de * Michael Pfeiffer */ #include "BootManagerController.h" #include #include #include #include #include #include #include #include #include "BootDrive.h" #include "DefaultPartitionPage.h" #include "DescriptionPage.h" #include "DrivesPage.h" #include "FileSelectionPage.h" #include "LegacyBootMenu.h" #include "PartitionsPage.h" #include "WizardView.h" #undef B_TRANSLATION_CONTEXT #define B_TRANSLATION_CONTEXT "BootManagerController" BootManagerController::BootManagerController() : fBootDrive(NULL), fBootMenu(NULL) { // set defaults fSettings.AddBool("install", true); fSettings.AddInt32("defaultPartition", 0); fSettings.AddInt32("timeout", -1); BPath path; if (find_directory(B_USER_SETTINGS_DIRECTORY, &path, true) == B_OK) { path.Append("bootman/MBR"); fSettings.AddString("file", path.Path()); // create directory BPath parent; if (path.GetParent(&parent) == B_OK) { BDirectory directory; directory.CreateDirectory(parent.Path(), NULL); } } else { fSettings.AddString("file", ""); } // That's the only boot menu we support at the moment. fBootMenus.AddItem(new LegacyBootMenu()); } BootManagerController::~BootManagerController() { } void BootManagerController::Previous(WizardView* wizard) { if (CurrentState() != kStateEntry) WizardController::Previous(wizard); else { fSettings.ReplaceBool("install", false); WizardController::Next(wizard); } } int32 BootManagerController::InitialState() { return kStateEntry; } int32 BootManagerController::NextState(int32 state) { switch (state) { case kStateEntry: { const char* path; if (fSettings.FindString("disk", &path) != B_OK) return kStateErrorEntry; delete fBootDrive; fBootDrive = new BootDrive(path); fBootMenu = fBootDrive->InstalledMenu(fBootMenus); if (fSettings.FindBool("install")) { int32 nextState = fBootMenu != NULL ? kStatePartitions : kStateSaveMBR; // TODO: call BootDrive::AddSupportedMenus() once we support // more than one type of boot menu - we'll probably need a // requester to choose from them then as well. if (fBootMenu == NULL) fBootMenu = fBootMenus.ItemAt(0); fCollectPartitionsStatus = fBootMenu->CollectPartitions( *fBootDrive, fSettings); return nextState; } return kStateUninstall; } case kStateErrorEntry: be_app->PostMessage(B_QUIT_REQUESTED); break; case kStateSaveMBR: if (_SaveMBR()) return kStateMBRSaved; break; case kStateMBRSaved: return kStatePartitions; case kStatePartitions: if (_HasSelectedPartitions()) return kStateDefaultPartitions; break; case kStateDefaultPartitions: return kStateInstallSummary; case kStateInstallSummary: if (_WriteBootMenu()) return kStateInstalled; break; case kStateInstalled: be_app->PostMessage(B_QUIT_REQUESTED); break; case kStateUninstall: if (_RestoreMBR()) return kStateUninstalled; break; case kStateUninstalled: be_app->PostMessage(B_QUIT_REQUESTED); break; } // cannot leave the current state/page return -1; } bool BootManagerController::_HasSelectedPartitions() { BMessage message; for (int32 i = 0; fSettings.FindMessage("partition", i, &message) == B_OK; i++) { bool show; if (message.FindBool("show", &show) == B_OK && show) return true; } BAlert* alert = new BAlert("info", B_TRANSLATE("At least one partition must be selected!"), B_TRANSLATE_COMMENT("OK", "Button")); alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); alert->Go(); return false; } bool BootManagerController::_WriteBootMenu() { BAlert* alert = new BAlert("confirm", B_TRANSLATE("About to write the " "boot menu to disk. Are you sure you want to continue?"), B_TRANSLATE_COMMENT("Write boot menu", "Button"), B_TRANSLATE_COMMENT("Back", "Button"), NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT); if (alert->Go() == 1) return false; fWriteBootMenuStatus = fBootMenu->Install(*fBootDrive, fSettings); return true; } bool BootManagerController::_SaveMBR() { BString path; fSettings.FindString("file", &path); BFile file(path.String(), B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE); fSaveMBRStatus = fBootMenu->SaveMasterBootRecord(&fSettings, &file); return true; } bool BootManagerController::_RestoreMBR() { BString disk; BString path; fSettings.FindString("disk", &disk); fSettings.FindString("file", &path); BString message; message << B_TRANSLATE_COMMENT("About to restore the Master Boot Record " "(MBR) of %disk from %file. Do you wish to continue?", "Don't translate the place holders: %disk and %file"); message.ReplaceFirst("%disk", disk); message.ReplaceFirst("%file", path); BAlert* alert = new BAlert("confirm", message.String(), B_TRANSLATE_COMMENT("Restore MBR", "Button"), B_TRANSLATE_COMMENT("Back", "Button"), NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT); if (alert->Go() == 1) return false; BFile file(path.String(), B_READ_ONLY); fRestoreMBRStatus = fBootMenu->RestoreMasterBootRecord(&fSettings, &file); return true; } WizardPageView* BootManagerController::CreatePage(int32 state, WizardView* wizard) { WizardPageView* page = NULL; BRect frame(0, 0, 300, 250); switch (state) { case kStateEntry: fSettings.ReplaceBool("install", true); page = new DrivesPage(wizard, fBootMenus, &fSettings, "drives"); break; case kStateErrorEntry: page = _CreateErrorEntryPage(); wizard->SetPreviousButtonHidden(true); wizard->SetNextButtonLabel(B_TRANSLATE_COMMENT("Done", "Button")); break; case kStateSaveMBR: page = _CreateSaveMBRPage(frame); wizard->SetPreviousButtonHidden(false); break; case kStateMBRSaved: page = _CreateMBRSavedPage(); break; case kStatePartitions: page = new PartitionsPage(&fSettings, "partitions"); wizard->SetPreviousButtonHidden(false); break; case kStateDefaultPartitions: page = new DefaultPartitionPage(&fSettings, frame, "default"); break; case kStateInstallSummary: page = _CreateInstallSummaryPage(); break; case kStateInstalled: page = _CreateInstalledPage(); wizard->SetNextButtonLabel(B_TRANSLATE_COMMENT("Done", "Button")); break; case kStateUninstall: page = _CreateUninstallPage(frame); wizard->SetPreviousButtonHidden(false); break; case kStateUninstalled: // TODO prevent overwriting MBR after clicking "Previous" page = _CreateUninstalledPage(); wizard->SetNextButtonLabel(B_TRANSLATE_COMMENT("Done", "Button")); break; } return page; } WizardPageView* BootManagerController::_CreateErrorEntryPage() { BString description; if (fCollectPartitionsStatus == B_ENTRY_NOT_FOUND) { description << B_TRANSLATE_COMMENT("Partition table not compatible", "Title") << "\n\n" << B_TRANSLATE("The partition table of the first hard disk is not " "compatible with Boot Manager.\n" "Boot Manager only works with IBM PC MBR partitions."); } else if (fCollectPartitionsStatus == B_PARTITION_TOO_SMALL) { description << B_TRANSLATE_COMMENT("First partition starts too early", "Title") << "\n\n" << B_TRANSLATE("The first partition on the disk starts too early " "and does not leave enough space free for a boot menu.\n" "Boot Manager needs 2 KiB available space before the first " "partition."); } else { description << B_TRANSLATE_COMMENT("Error reading partition table", "Title") << "\n\n" << B_TRANSLATE("Boot Manager is unable to read the partition " "table!"); } return new DescriptionPage("errorEntry", description.String(), true); } WizardPageView* BootManagerController::_CreateSaveMBRPage(BRect frame) { BString description; BString disk; fSettings.FindString("disk", &disk); description << B_TRANSLATE_COMMENT("Backup Master Boot Record", "Title") << "\n" << B_TRANSLATE("The Master Boot Record (MBR) of the boot " "device:\n" "\t%s\n" "will now be saved to disk. Please select a file to " "save the MBR into.\n\n" "If something goes wrong with the installation or if " "you later wish to remove the boot menu, simply run the " "bootman program and choose the 'Uninstall' option."); description.ReplaceFirst("%s", disk); return new FileSelectionPage(&fSettings, frame, "saveMBR", description.String(), B_SAVE_PANEL); } WizardPageView* BootManagerController::_CreateMBRSavedPage() { BString description; BString file; fSettings.FindString("file", &file); if (fSaveMBRStatus == B_OK) { description << B_TRANSLATE_COMMENT("Old Master Boot Record saved", "Title") << "\n" << B_TRANSLATE("The old Master Boot Record was successfully " "saved to %s.") << "\n"; } else { description << B_TRANSLATE_COMMENT("Old Master Boot Record backup " "failure", "Title") << "\n" << B_TRANSLATE("The old Master Boot Record could not be saved " "to %s. You can continue the installation but there will be no " "way to uninstall the boot menu.") << "\n"; } description.ReplaceFirst("%s", file); return new DescriptionPage("summary", description.String(), true); } WizardPageView* BootManagerController::_CreateInstallSummaryPage() { BString description; BString disk; fSettings.FindString("disk", &disk); description << B_TRANSLATE_COMMENT("Summary", "Title") << "\n" << B_TRANSLATE("About to write the following boot menu to the boot " "disk (%s). Please verify the information below before continuing.") << "\n\n"; description.ReplaceFirst("%s", disk); BMessage message; for (int32 i = 0; fSettings.FindMessage("partition", i, &message) == B_OK; i++) { bool show; if (message.FindBool("show", &show) != B_OK || !show) continue; BString name; BString path; message.FindString("name", &name); message.FindString("path", &path); BString displayName; if (fBootMenu->GetDisplayText(name.String(), displayName) == B_OK) description << displayName << "\t(" << path << ")\n"; else description << name << "\t(" << path << ")\n"; } return new DescriptionPage("summary", description.String(), true); } WizardPageView* BootManagerController::_CreateInstalledPage() { BString description; if (fWriteBootMenuStatus == B_OK) { description << B_TRANSLATE_COMMENT("Installation of boot menu " "completed", "Title") << "\n" << B_TRANSLATE("The boot manager has been successfully installed " "on your system."); } else { description << B_TRANSLATE_COMMENT("Installation of boot menu failed", "Title") << "\n" << B_TRANSLATE("An error occurred writing the boot menu. " "The Master Boot Record might be destroyed, " "you should restore the MBR now!"); } return new DescriptionPage("done", description, true); } WizardPageView* BootManagerController::_CreateUninstallPage(BRect frame) { BString description; description << B_TRANSLATE_COMMENT("Uninstall boot manager", "Title") << "\n\n" << B_TRANSLATE("Please locate the Master Boot Record (MBR) save file " "to restore from. This is the file that was created when the " "boot manager was first installed."); return new FileSelectionPage(&fSettings, frame, "restoreMBR", description.String(), B_OPEN_PANEL); } WizardPageView* BootManagerController::_CreateUninstalledPage() { BString description; BString disk; BString file; fSettings.FindString("disk", &disk); fSettings.FindString("file", &file); if (fRestoreMBRStatus == B_OK) { description << B_TRANSLATE_COMMENT("Uninstallation of boot menu " "completed", "Title") << "\n" << B_TRANSLATE("The Master Boot Record of the boot device " "(%DISK) has been successfully restored from %FILE."); description.ReplaceFirst("%DISK", disk); description.ReplaceFirst("%FILE", file); } else { description << B_TRANSLATE_COMMENT("Uninstallation of boot menu " "failed", "Title") << "\n" << B_TRANSLATE("The Master Boot Record could not be restored!"); } return new DescriptionPage("summary", description.String(), true); }