1/* 2 * Copyright 2008-2011, Haiku, Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Axel D��rfler, axeld@pinc-software.de 7 * Michael Pfeiffer <laplace@users.sourceforge.net> 8 */ 9 10 11#include "BootManagerController.h" 12 13#include <Alert.h> 14#include <Application.h> 15#include <Catalog.h> 16#include <File.h> 17#include <FindDirectory.h> 18#include <Locale.h> 19#include <Path.h> 20#include <String.h> 21 22#include "BootDrive.h" 23#include "DefaultPartitionPage.h" 24#include "DescriptionPage.h" 25#include "DrivesPage.h" 26#include "FileSelectionPage.h" 27#include "LegacyBootMenu.h" 28#include "PartitionsPage.h" 29#include "WizardView.h" 30 31 32#undef B_TRANSLATION_CONTEXT 33#define B_TRANSLATION_CONTEXT "BootManagerController" 34 35 36BootManagerController::BootManagerController() 37 : 38 fBootDrive(NULL), 39 fBootMenu(NULL) 40{ 41 // set defaults 42 fSettings.AddBool("install", true); 43 fSettings.AddInt32("defaultPartition", 0); 44 fSettings.AddInt32("timeout", -1); 45 46 BPath path; 47 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path, true) == B_OK) { 48 path.Append("bootman/MBR"); 49 fSettings.AddString("file", path.Path()); 50 // create directory 51 BPath parent; 52 if (path.GetParent(&parent) == B_OK) { 53 BDirectory directory; 54 directory.CreateDirectory(parent.Path(), NULL); 55 } 56 } else { 57 fSettings.AddString("file", ""); 58 } 59 60 // That's the only boot menu we support at the moment. 61 fBootMenus.AddItem(new LegacyBootMenu()); 62} 63 64 65BootManagerController::~BootManagerController() 66{ 67} 68 69 70void 71BootManagerController::Previous(WizardView* wizard) 72{ 73 if (CurrentState() != kStateEntry) 74 WizardController::Previous(wizard); 75 else { 76 fSettings.ReplaceBool("install", false); 77 WizardController::Next(wizard); 78 } 79} 80 81 82int32 83BootManagerController::InitialState() 84{ 85 return kStateEntry; 86} 87 88 89int32 90BootManagerController::NextState(int32 state) 91{ 92 switch (state) { 93 case kStateEntry: 94 { 95 const char* path; 96 if (fSettings.FindString("disk", &path) != B_OK) 97 return kStateErrorEntry; 98 99 delete fBootDrive; 100 101 fBootDrive = new BootDrive(path); 102 fBootMenu = fBootDrive->InstalledMenu(fBootMenus); 103 104 if (fSettings.FindBool("install")) { 105 int32 nextState = fBootMenu != NULL 106 ? kStatePartitions : kStateSaveMBR; 107 108 // TODO: call BootDrive::AddSupportedMenus() once we support 109 // more than one type of boot menu - we'll probably need a 110 // requester to choose from them then as well. 111 if (fBootMenu == NULL) 112 fBootMenu = fBootMenus.ItemAt(0); 113 114 fCollectPartitionsStatus = fBootMenu->CollectPartitions( 115 *fBootDrive, fSettings); 116 117 return nextState; 118 } 119 120 return kStateUninstall; 121 } 122 123 case kStateErrorEntry: 124 be_app->PostMessage(B_QUIT_REQUESTED); 125 break; 126 127 case kStateSaveMBR: 128 if (_SaveMBR()) 129 return kStateMBRSaved; 130 break; 131 132 case kStateMBRSaved: 133 return kStatePartitions; 134 135 case kStatePartitions: 136 if (_HasSelectedPartitions()) 137 return kStateDefaultPartitions; 138 break; 139 140 case kStateDefaultPartitions: 141 return kStateInstallSummary; 142 143 case kStateInstallSummary: 144 if (_WriteBootMenu()) 145 return kStateInstalled; 146 break; 147 148 case kStateInstalled: 149 be_app->PostMessage(B_QUIT_REQUESTED); 150 break; 151 152 case kStateUninstall: 153 if (_RestoreMBR()) 154 return kStateUninstalled; 155 break; 156 157 case kStateUninstalled: 158 be_app->PostMessage(B_QUIT_REQUESTED); 159 break; 160 } 161 // cannot leave the current state/page 162 return -1; 163} 164 165 166bool 167BootManagerController::_HasSelectedPartitions() 168{ 169 BMessage message; 170 for (int32 i = 0; fSettings.FindMessage("partition", i, &message) == B_OK; 171 i++) { 172 bool show; 173 if (message.FindBool("show", &show) == B_OK && show) 174 return true; 175 } 176 177 BAlert* alert = new BAlert("info", 178 B_TRANSLATE("At least one partition must be selected!"), 179 B_TRANSLATE_COMMENT("OK", "Button")); 180 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 181 alert->Go(); 182 183 return false; 184} 185 186 187bool 188BootManagerController::_WriteBootMenu() 189{ 190 BAlert* alert = new BAlert("confirm", B_TRANSLATE("About to write the " 191 "boot menu to disk. Are you sure you want to continue?"), 192 B_TRANSLATE_COMMENT("Write boot menu", "Button"), 193 B_TRANSLATE_COMMENT("Back", "Button"), NULL, B_WIDTH_AS_USUAL, 194 B_WARNING_ALERT); 195 196 if (alert->Go() == 1) 197 return false; 198 199 fWriteBootMenuStatus = fBootMenu->Install(*fBootDrive, fSettings); 200 return true; 201} 202 203 204bool 205BootManagerController::_SaveMBR() 206{ 207 BString path; 208 fSettings.FindString("file", &path); 209 BFile file(path.String(), B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE); 210 fSaveMBRStatus = fBootMenu->SaveMasterBootRecord(&fSettings, &file); 211 return true; 212} 213 214 215bool 216BootManagerController::_RestoreMBR() 217{ 218 BString disk; 219 BString path; 220 fSettings.FindString("disk", &disk); 221 fSettings.FindString("file", &path); 222 223 BString message; 224 message << B_TRANSLATE_COMMENT("About to restore the Master Boot Record " 225 "(MBR) of %disk from %file. Do you wish to continue?", 226 "Don't translate the place holders: %disk and %file"); 227 message.ReplaceFirst("%disk", disk); 228 message.ReplaceFirst("%file", path); 229 230 BAlert* alert = new BAlert("confirm", message.String(), 231 B_TRANSLATE_COMMENT("Restore MBR", "Button"), 232 B_TRANSLATE_COMMENT("Back", "Button"), 233 NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 234 if (alert->Go() == 1) 235 return false; 236 237 BFile file(path.String(), B_READ_ONLY); 238 fRestoreMBRStatus = fBootMenu->RestoreMasterBootRecord(&fSettings, &file); 239 return true; 240} 241 242 243WizardPageView* 244BootManagerController::CreatePage(int32 state, WizardView* wizard) 245{ 246 WizardPageView* page = NULL; 247 BRect frame(0, 0, 300, 250); 248 249 switch (state) { 250 case kStateEntry: 251 fSettings.ReplaceBool("install", true); 252 page = new DrivesPage(wizard, fBootMenus, &fSettings, "drives"); 253 break; 254 case kStateErrorEntry: 255 page = _CreateErrorEntryPage(); 256 wizard->SetPreviousButtonHidden(true); 257 wizard->SetNextButtonLabel(B_TRANSLATE_COMMENT("Done", "Button")); 258 break; 259 case kStateSaveMBR: 260 page = _CreateSaveMBRPage(frame); 261 wizard->SetPreviousButtonHidden(false); 262 break; 263 case kStateMBRSaved: 264 page = _CreateMBRSavedPage(); 265 break; 266 case kStatePartitions: 267 page = new PartitionsPage(&fSettings, "partitions"); 268 wizard->SetPreviousButtonHidden(false); 269 break; 270 case kStateDefaultPartitions: 271 page = new DefaultPartitionPage(&fSettings, frame, "default"); 272 break; 273 case kStateInstallSummary: 274 page = _CreateInstallSummaryPage(); 275 break; 276 case kStateInstalled: 277 page = _CreateInstalledPage(); 278 wizard->SetNextButtonLabel(B_TRANSLATE_COMMENT("Done", "Button")); 279 break; 280 case kStateUninstall: 281 page = _CreateUninstallPage(frame); 282 wizard->SetPreviousButtonHidden(false); 283 break; 284 case kStateUninstalled: 285 // TODO prevent overwriting MBR after clicking "Previous" 286 page = _CreateUninstalledPage(); 287 wizard->SetNextButtonLabel(B_TRANSLATE_COMMENT("Done", "Button")); 288 break; 289 } 290 291 return page; 292} 293 294 295WizardPageView* 296BootManagerController::_CreateErrorEntryPage() 297{ 298 BString description; 299 300 if (fCollectPartitionsStatus == B_ENTRY_NOT_FOUND) { 301 description << B_TRANSLATE_COMMENT("Partition table not compatible", 302 "Title") << "\n\n" 303 << B_TRANSLATE("The partition table of the first hard disk is not " 304 "compatible with Boot Manager.\n" 305 "Boot Manager only works with IBM PC MBR partitions."); 306 } else if (fCollectPartitionsStatus == B_PARTITION_TOO_SMALL) { 307 description << B_TRANSLATE_COMMENT("First partition starts too early", 308 "Title") << "\n\n" 309 << B_TRANSLATE("The first partition on the disk starts too early " 310 "and does not leave enough space free for a boot menu.\n" 311 "Boot Manager needs 2 KiB available space before the first " 312 "partition."); 313 } else { 314 description << B_TRANSLATE_COMMENT("Error reading partition table", 315 "Title") << "\n\n" 316 << B_TRANSLATE("Boot Manager is unable to read the partition " 317 "table!"); 318 } 319 320 return new DescriptionPage("errorEntry", description.String(), true); 321} 322 323 324WizardPageView* 325BootManagerController::_CreateSaveMBRPage(BRect frame) 326{ 327 BString description; 328 BString disk; 329 fSettings.FindString("disk", &disk); 330 331 description << B_TRANSLATE_COMMENT("Backup Master Boot Record", "Title") 332 << "\n" << B_TRANSLATE("The Master Boot Record (MBR) of the boot " 333 "device:\n" 334 "\t%s\n" 335 "will now be saved to disk. Please select a file to " 336 "save the MBR into.\n\n" 337 "If something goes wrong with the installation or if " 338 "you later wish to remove the boot menu, simply run the " 339 "bootman program and choose the 'Uninstall' option."); 340 description.ReplaceFirst("%s", disk); 341 342 return new FileSelectionPage(&fSettings, frame, "saveMBR", 343 description.String(), 344 B_SAVE_PANEL); 345} 346 347 348WizardPageView* 349BootManagerController::_CreateMBRSavedPage() 350{ 351 BString description; 352 BString file; 353 fSettings.FindString("file", &file); 354 355 if (fSaveMBRStatus == B_OK) { 356 description << B_TRANSLATE_COMMENT("Old Master Boot Record saved", 357 "Title") << "\n" 358 << B_TRANSLATE("The old Master Boot Record was successfully " 359 "saved to %s.") << "\n"; 360 } else { 361 description << B_TRANSLATE_COMMENT("Old Master Boot Record backup " 362 "failure", "Title") << "\n" 363 << B_TRANSLATE("The old Master Boot Record could not be saved " 364 "to %s. You can continue the installation but there will be no " 365 "way to uninstall the boot menu.") << "\n"; 366 } 367 description.ReplaceFirst("%s", file); 368 369 return new DescriptionPage("summary", description.String(), true); 370} 371 372 373WizardPageView* 374BootManagerController::_CreateInstallSummaryPage() 375{ 376 BString description; 377 BString disk; 378 fSettings.FindString("disk", &disk); 379 380 description << B_TRANSLATE_COMMENT("Summary", "Title") << "\n" 381 << B_TRANSLATE("About to write the following boot menu to the boot " 382 "disk (%s). Please verify the information below before continuing.") 383 << "\n\n"; 384 description.ReplaceFirst("%s", disk); 385 386 BMessage message; 387 for (int32 i = 0; fSettings.FindMessage("partition", i, &message) == B_OK; 388 i++) { 389 bool show; 390 if (message.FindBool("show", &show) != B_OK || !show) 391 continue; 392 393 BString name; 394 BString path; 395 message.FindString("name", &name); 396 message.FindString("path", &path); 397 398 BString displayName; 399 if (fBootMenu->GetDisplayText(name.String(), displayName) == B_OK) 400 description << displayName << "\t(" << path << ")\n"; 401 else 402 description << name << "\t(" << path << ")\n"; 403 } 404 405 return new DescriptionPage("summary", description.String(), true); 406} 407 408 409WizardPageView* 410BootManagerController::_CreateInstalledPage() 411{ 412 BString description; 413 414 if (fWriteBootMenuStatus == B_OK) { 415 description << B_TRANSLATE_COMMENT("Installation of boot menu " 416 "completed", "Title") << "\n" 417 << B_TRANSLATE("The boot manager has been successfully installed " 418 "on your system."); 419 } else { 420 description << B_TRANSLATE_COMMENT("Installation of boot menu failed", 421 "Title") << "\n" 422 << B_TRANSLATE("An error occurred writing the boot menu. " 423 "The Master Boot Record might be destroyed, " 424 "you should restore the MBR now!"); 425 } 426 427 return new DescriptionPage("done", description, true); 428} 429 430 431WizardPageView* 432BootManagerController::_CreateUninstallPage(BRect frame) 433{ 434 BString description; 435 description << B_TRANSLATE_COMMENT("Uninstall boot manager", "Title") 436 << "\n\n" 437 << B_TRANSLATE("Please locate the Master Boot Record (MBR) save file " 438 "to restore from. This is the file that was created when the " 439 "boot manager was first installed."); 440 441 return new FileSelectionPage(&fSettings, frame, "restoreMBR", 442 description.String(), B_OPEN_PANEL); 443} 444 445 446WizardPageView* 447BootManagerController::_CreateUninstalledPage() 448{ 449 BString description; 450 BString disk; 451 BString file; 452 fSettings.FindString("disk", &disk); 453 fSettings.FindString("file", &file); 454 455 if (fRestoreMBRStatus == B_OK) { 456 description << B_TRANSLATE_COMMENT("Uninstallation of boot menu " 457 "completed", "Title") << "\n" 458 << B_TRANSLATE("The Master Boot Record of the boot device " 459 "(%DISK) has been successfully restored from %FILE."); 460 description.ReplaceFirst("%DISK", disk); 461 description.ReplaceFirst("%FILE", file); 462 } else { 463 description << B_TRANSLATE_COMMENT("Uninstallation of boot menu " 464 "failed", "Title") << "\n" 465 << B_TRANSLATE("The Master Boot Record could not be restored!"); 466 } 467 468 return new DescriptionPage("summary", description.String(), true); 469} 470