xref: /haiku/src/apps/bootmanager/BootManagerController.cpp (revision 46d4471af7fad4e52cfbd09174598cf5318aceed)
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