xref: /haiku/src/apps/bootmanager/BootManagerController.cpp (revision 2824c5d3a428b1f52fb41e7cf84ef1690cfadc80)
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 
BootManagerController()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 
~BootManagerController()65 BootManagerController::~BootManagerController()
66 {
67 }
68 
69 
70 void
Previous(WizardView * wizard)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
InitialState()83 BootManagerController::InitialState()
84 {
85 	return kStateEntry;
86 }
87 
88 
89 int32
NextState(int32 state)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
_HasSelectedPartitions()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
_WriteBootMenu()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
_SaveMBR()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
_RestoreMBR()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*
CreatePage(int32 state,WizardView * wizard)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*
_CreateErrorEntryPage()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*
_CreateSaveMBRPage(BRect frame)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*
_CreateMBRSavedPage()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 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 
373 WizardPageView*
_CreateInstallSummaryPage()374 BootManagerController::_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 
409 WizardPageView*
_CreateInstalledPage()410 BootManagerController::_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 
431 WizardPageView*
_CreateUninstallPage(BRect frame)432 BootManagerController::_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 
446 WizardPageView*
_CreateUninstalledPage()447 BootManagerController::_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