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 36 BootManagerController::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 65 BootManagerController::~BootManagerController() 66 { 67 } 68 69 70 void 71 BootManagerController::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 82 int32 83 BootManagerController::InitialState() 84 { 85 return kStateEntry; 86 } 87 88 89 int32 90 BootManagerController::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 166 bool 167 BootManagerController::_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 187 bool 188 BootManagerController::_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 204 bool 205 BootManagerController::_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 215 bool 216 BootManagerController::_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 243 WizardPageView* 244 BootManagerController::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 295 WizardPageView* 296 BootManagerController::_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 324 WizardPageView* 325 BootManagerController::_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 348 WizardPageView* 349 BootManagerController::_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 Saved " 362 "failure", "Title") << "\n" 363 << B_TRANSLATE("The old Master Boot Record could not be saved " 364 "to %s") << "\n"; 365 } 366 description.ReplaceFirst("%s", file); 367 368 return new DescriptionPage("summary", description.String(), true); 369 } 370 371 372 WizardPageView* 373 BootManagerController::_CreateInstallSummaryPage() 374 { 375 BString description; 376 BString disk; 377 fSettings.FindString("disk", &disk); 378 379 description << B_TRANSLATE_COMMENT("Summary", "Title") << "\n" 380 << B_TRANSLATE("About to write the following boot menu to the boot " 381 "disk (%s). Please verify the information below before continuing.") 382 << "\n\n"; 383 description.ReplaceFirst("%s", disk); 384 385 BMessage message; 386 for (int32 i = 0; fSettings.FindMessage("partition", i, &message) == B_OK; 387 i++) { 388 bool show; 389 if (message.FindBool("show", &show) != B_OK || !show) 390 continue; 391 392 BString name; 393 BString path; 394 message.FindString("name", &name); 395 message.FindString("path", &path); 396 397 BString displayName; 398 if (fBootMenu->GetDisplayText(name.String(), displayName) == B_OK) 399 description << displayName << "\t(" << path << ")\n"; 400 else 401 description << name << "\t(" << path << ")\n"; 402 } 403 404 return new DescriptionPage("summary", description.String(), true); 405 } 406 407 408 WizardPageView* 409 BootManagerController::_CreateInstalledPage() 410 { 411 BString description; 412 413 if (fWriteBootMenuStatus == B_OK) { 414 description << B_TRANSLATE_COMMENT("Installation of boot menu " 415 "completed", "Title") << "\n" 416 << B_TRANSLATE("The boot manager has been successfully installed " 417 "on your system."); 418 } else { 419 description << B_TRANSLATE_COMMENT("Installation of boot menu failed", 420 "Title") << "\n" 421 << B_TRANSLATE("An error occurred writing the boot menu. " 422 "The Master Boot Record might be destroyed, " 423 "you should restore the MBR now!"); 424 } 425 426 return new DescriptionPage("done", description, true); 427 } 428 429 430 WizardPageView* 431 BootManagerController::_CreateUninstallPage(BRect frame) 432 { 433 BString description; 434 description << B_TRANSLATE_COMMENT("Uninstall boot manager", "Title") 435 << "\n\n" 436 << B_TRANSLATE("Please locate the Master Boot Record (MBR) save file " 437 "to restore from. This is the file that was created when the " 438 "boot manager was first installed."); 439 440 return new FileSelectionPage(&fSettings, frame, "restoreMBR", 441 description.String(), B_OPEN_PANEL); 442 } 443 444 445 WizardPageView* 446 BootManagerController::_CreateUninstalledPage() 447 { 448 BString description; 449 BString disk; 450 BString file; 451 fSettings.FindString("disk", &disk); 452 fSettings.FindString("file", &file); 453 454 if (fRestoreMBRStatus == B_OK) { 455 description << B_TRANSLATE_COMMENT("Uninstallation of boot menu " 456 "completed", "Title") << "\n" 457 << B_TRANSLATE("The Master Boot Record of the boot device " 458 "(%DISK) has been successfully restored from %FILE."); 459 description.ReplaceFirst("%DISK", disk); 460 description.ReplaceFirst("%FILE", file); 461 } else { 462 description << B_TRANSLATE_COMMENT("Uninstallation of boot menu " 463 "failed", "Title") << "\n" 464 << B_TRANSLATE("The Master Boot Record could not be restored!"); 465 } 466 467 return new DescriptionPage("summary", description.String(), true); 468 } 469