xref: /haiku/src/system/boot/loader/menu.cpp (revision 21258e2674226d6aa732321b6f8494841895af5f)
1 /*
2  * Copyright 2003-2013, Axel Dörfler, axeld@pinc-software.de.
3  * Copyright 2011, Rene Gollent, rene@gollent.com.
4  * Copyright 2013-2014, Ingo Weinhold, ingo_weinhold@gmx.de.
5  * Distributed under the terms of the MIT License.
6  */
7 
8 
9 #include "menu.h"
10 
11 #include <errno.h>
12 #include <strings.h>
13 
14 #include <algorithm>
15 
16 #include <OS.h>
17 
18 #include <AutoDeleter.h>
19 #include <boot/menu.h>
20 #include <boot/PathBlacklist.h>
21 #include <boot/stage2.h>
22 #include <boot/vfs.h>
23 #include <boot/platform.h>
24 #include <boot/platform/generic/text_console.h>
25 #include <boot/stdio.h>
26 #include <safemode.h>
27 #include <util/ring_buffer.h>
28 #include <util/SinglyLinkedList.h>
29 
30 #include "kernel_debug_config.h"
31 
32 #include "load_driver_settings.h"
33 #include "loader.h"
34 #include "package_support.h"
35 #include "pager.h"
36 #include "RootFileSystem.h"
37 
38 
39 //#define TRACE_MENU
40 #ifdef TRACE_MENU
41 #	define TRACE(x) dprintf x
42 #else
43 #	define TRACE(x) ;
44 #endif
45 
46 
47 // only set while in user_menu()
48 static Menu* sMainMenu = NULL;
49 static Menu* sBlacklistRootMenu = NULL;
50 static BootVolume* sBootVolume = NULL;
51 static PathBlacklist* sPathBlacklist;
52 
53 
54 MenuItem::MenuItem(const char *label, Menu *subMenu)
55 	:
56 	fLabel(strdup(label)),
57 	fTarget(NULL),
58 	fIsMarked(false),
59 	fIsSelected(false),
60 	fIsEnabled(true),
61 	fType(MENU_ITEM_STANDARD),
62 	fMenu(NULL),
63 	fSubMenu(NULL),
64 	fData(NULL),
65 	fHelpText(NULL),
66 	fShortcut(0)
67 {
68 	SetSubmenu(subMenu);
69 }
70 
71 
72 MenuItem::~MenuItem()
73 {
74 	delete fSubMenu;
75 	free(const_cast<char *>(fLabel));
76 }
77 
78 
79 void
80 MenuItem::SetTarget(menu_item_hook target)
81 {
82 	fTarget = target;
83 }
84 
85 
86 /**	Marks or unmarks a menu item. A marked menu item usually gets a visual
87  *	clue like a checkmark that distinguishes it from others.
88  *	For menus of type CHOICE_MENU, there can only be one marked item - the
89  *	chosen one.
90  */
91 
92 void
93 MenuItem::SetMarked(bool marked)
94 {
95 	if (marked && fMenu != NULL && fMenu->Type() == CHOICE_MENU) {
96 		// always set choice text of parent if we were marked
97 		fMenu->SetChoiceText(Label());
98 	}
99 
100 	if (fIsMarked == marked)
101 		return;
102 
103 	if (marked && fMenu != NULL && fMenu->Type() == CHOICE_MENU) {
104 		// unmark previous item
105 		MenuItem *markedItem = fMenu->FindMarked();
106 		if (markedItem != NULL)
107 			markedItem->SetMarked(false);
108 	}
109 
110 	fIsMarked = marked;
111 
112 	if (fMenu != NULL)
113 		fMenu->Draw(this);
114 }
115 
116 
117 void
118 MenuItem::Select(bool selected)
119 {
120 	if (fIsSelected == selected)
121 		return;
122 
123 	if (selected && fMenu != NULL) {
124 		// unselect previous item
125 		MenuItem *selectedItem = fMenu->FindSelected();
126 		if (selectedItem != NULL)
127 			selectedItem->Select(false);
128 	}
129 
130 	fIsSelected = selected;
131 
132 	if (fMenu != NULL)
133 		fMenu->Draw(this);
134 }
135 
136 
137 void
138 MenuItem::SetType(menu_item_type type)
139 {
140 	fType = type;
141 }
142 
143 
144 void
145 MenuItem::SetEnabled(bool enabled)
146 {
147 	if (fIsEnabled == enabled)
148 		return;
149 
150 	fIsEnabled = enabled;
151 
152 	if (fMenu != NULL)
153 		fMenu->Draw(this);
154 }
155 
156 
157 void
158 MenuItem::SetData(const void *data)
159 {
160 	fData = data;
161 }
162 
163 
164 /*!	This sets a help text that is shown when the item is
165 	selected.
166 	Note, unlike the label, the string is not copied, it's
167 	just referenced and has to stay valid as long as the
168 	item's menu is being used.
169 */
170 void
171 MenuItem::SetHelpText(const char* text)
172 {
173 	fHelpText = text;
174 }
175 
176 
177 void
178 MenuItem::SetShortcut(char key)
179 {
180 	fShortcut = key;
181 }
182 
183 
184 void
185 MenuItem::SetLabel(const char* label)
186 {
187 	if (char* newLabel = strdup(label)) {
188 		free(const_cast<char*>(fLabel));
189 		fLabel = newLabel;
190 	}
191 }
192 
193 
194 void
195 MenuItem::SetSubmenu(Menu* subMenu)
196 {
197 	fSubMenu = subMenu;
198 
199 	if (fSubMenu != NULL)
200 		fSubMenu->fSuperItem = this;
201 }
202 
203 
204 void
205 MenuItem::SetMenu(Menu* menu)
206 {
207 	fMenu = menu;
208 }
209 
210 
211 //	#pragma mark -
212 
213 
214 Menu::Menu(menu_type type, const char* title)
215 	:
216 	fTitle(title),
217 	fChoiceText(NULL),
218 	fCount(0),
219 	fIsHidden(true),
220 	fType(type),
221 	fSuperItem(NULL),
222 	fShortcuts(NULL)
223 {
224 }
225 
226 
227 Menu::~Menu()
228 {
229 	// take all remaining items with us
230 
231 	MenuItem *item;
232 	while ((item = fItems.Head()) != NULL) {
233 		fItems.Remove(item);
234 		delete item;
235 	}
236 }
237 
238 
239 void
240 Menu::Entered()
241 {
242 }
243 
244 
245 void
246 Menu::Exited()
247 {
248 }
249 
250 
251 MenuItem*
252 Menu::ItemAt(int32 index)
253 {
254 	if (index < 0 || index >= fCount)
255 		return NULL;
256 
257 	MenuItemIterator iterator = ItemIterator();
258 	MenuItem *item;
259 
260 	while ((item = iterator.Next()) != NULL) {
261 		if (index-- == 0)
262 			return item;
263 	}
264 
265 	return NULL;
266 }
267 
268 
269 int32
270 Menu::IndexOf(MenuItem* searchedItem)
271 {
272 	int32 index = 0;
273 
274 	MenuItemIterator iterator = ItemIterator();
275 	while (MenuItem* item = iterator.Next()) {
276 		if (item == searchedItem)
277 			return index;
278 
279 		index++;
280 	}
281 
282 	return -1;
283 }
284 
285 
286 int32
287 Menu::CountItems() const
288 {
289 	return fCount;
290 }
291 
292 
293 MenuItem*
294 Menu::FindItem(const char* label)
295 {
296 	MenuItemIterator iterator = ItemIterator();
297 	while (MenuItem* item = iterator.Next()) {
298 		if (item->Label() != NULL && !strcmp(item->Label(), label))
299 			return item;
300 	}
301 
302 	return NULL;
303 }
304 
305 
306 MenuItem*
307 Menu::FindMarked()
308 {
309 	MenuItemIterator iterator = ItemIterator();
310 	while (MenuItem* item = iterator.Next()) {
311 		if (item->IsMarked())
312 			return item;
313 	}
314 
315 	return NULL;
316 }
317 
318 
319 MenuItem*
320 Menu::FindSelected(int32* _index)
321 {
322 	int32 index = 0;
323 
324 	MenuItemIterator iterator = ItemIterator();
325 	while (MenuItem* item = iterator.Next()) {
326 		if (item->IsSelected()) {
327 			if (_index != NULL)
328 				*_index = index;
329 			return item;
330 		}
331 
332 		index++;
333 	}
334 
335 	return NULL;
336 }
337 
338 
339 void
340 Menu::AddItem(MenuItem* item)
341 {
342 	item->fMenu = this;
343 	fItems.Add(item);
344 	fCount++;
345 }
346 
347 
348 status_t
349 Menu::AddSeparatorItem()
350 {
351 	MenuItem* item = new(std::nothrow) MenuItem();
352 	if (item == NULL)
353 		return B_NO_MEMORY;
354 
355 	item->SetType(MENU_ITEM_SEPARATOR);
356 
357 	AddItem(item);
358 	return B_OK;
359 }
360 
361 
362 MenuItem*
363 Menu::RemoveItemAt(int32 index)
364 {
365 	if (index < 0 || index >= fCount)
366 		return NULL;
367 
368 	MenuItemIterator iterator = ItemIterator();
369 	while (MenuItem* item = iterator.Next()) {
370 		if (index-- == 0) {
371 			RemoveItem(item);
372 			return item;
373 		}
374 	}
375 
376 	return NULL;
377 }
378 
379 
380 void
381 Menu::RemoveItem(MenuItem* item)
382 {
383 	item->fMenu = NULL;
384 	fItems.Remove(item);
385 	fCount--;
386 }
387 
388 
389 void
390 Menu::AddShortcut(char key, shortcut_hook function)
391 {
392 	Menu::shortcut* shortcut = new(std::nothrow) Menu::shortcut;
393 	if (shortcut == NULL)
394 		return;
395 
396 	shortcut->key = key;
397 	shortcut->function = function;
398 
399 	shortcut->next = fShortcuts;
400 	fShortcuts = shortcut;
401 }
402 
403 
404 shortcut_hook
405 Menu::FindShortcut(char key) const
406 {
407 	if (key == 0)
408 		return NULL;
409 
410 	const Menu::shortcut* shortcut = fShortcuts;
411 	while (shortcut != NULL) {
412 		if (shortcut->key == key)
413 			return shortcut->function;
414 
415 		shortcut = shortcut->next;
416 	}
417 
418 	Menu *superMenu = Supermenu();
419 
420 	if (superMenu != NULL)
421 		return superMenu->FindShortcut(key);
422 
423 	return NULL;
424 }
425 
426 
427 MenuItem*
428 Menu::FindItemByShortcut(char key)
429 {
430 	if (key == 0)
431 		return NULL;
432 
433 	MenuItemList::Iterator iterator = ItemIterator();
434 	while (MenuItem* item = iterator.Next()) {
435 		if (item->Shortcut() == key)
436 			return item;
437 	}
438 
439 	Menu *superMenu = Supermenu();
440 
441 	if (superMenu != NULL)
442 		return superMenu->FindItemByShortcut(key);
443 
444 	return NULL;
445 }
446 
447 
448 void
449 Menu::SortItems(bool (*less)(const MenuItem*, const MenuItem*))
450 {
451 	fItems.Sort(less);
452 }
453 
454 
455 void
456 Menu::Run()
457 {
458 	platform_run_menu(this);
459 }
460 
461 
462 void
463 Menu::Draw(MenuItem* item)
464 {
465 	if (!IsHidden())
466 		platform_update_menu_item(this, item);
467 }
468 
469 
470 //	#pragma mark -
471 
472 
473 static const char*
474 size_to_string(off_t size, char* buffer, size_t bufferSize)
475 {
476 	static const char* const kPrefixes[] = { "K", "M", "G", "T", "P", NULL };
477 	int32 nextIndex = 0;
478 	int32 remainder = 0;
479 	while (size >= 1024 && kPrefixes[nextIndex] != NULL) {
480 		remainder = size % 1024;
481 		size /= 1024;
482 		nextIndex++;
483 
484 		if (size < 1024) {
485 			// Compute the decimal remainder and make sure we have at most
486 			// 3 decimal places (or 4 for 1000 <= size <= 1023).
487 			int32 factor;
488 			if (size >= 100)
489 				factor = 100;
490 			else if (size >= 10)
491 				factor = 10;
492 			else
493 				factor = 1;
494 
495 			remainder = (remainder * 1000 + 5 * factor) / 1024;
496 
497 			if (remainder >= 1000) {
498 				size++;
499 				remainder = 0;
500 			} else
501 				remainder /= 10 * factor;
502 		} else
503 			size += (remainder + 512) / 1024;
504 	}
505 
506 	if (remainder == 0) {
507 		snprintf(buffer, bufferSize, "%" B_PRIdOFF, size);
508 	} else {
509 		snprintf(buffer, bufferSize, "%" B_PRIdOFF ".%" B_PRId32, size,
510 			remainder);
511 	}
512 
513 	size_t length = strlen(buffer);
514 	snprintf(buffer + length, bufferSize - length, " %sB",
515 		nextIndex == 0 ? "" : kPrefixes[nextIndex - 1]);
516 
517 	return buffer;
518 }
519 
520 
521 // #pragma mark - blacklist menu
522 
523 
524 class BlacklistMenuItem : public MenuItem {
525 public:
526 	BlacklistMenuItem(char* label, Node* node, Menu* subMenu)
527 		:
528 		MenuItem(label, subMenu),
529 		fNode(node),
530 		fSubMenu(subMenu)
531 	{
532 		fNode->Acquire();
533 		SetType(MENU_ITEM_MARKABLE);
534 	}
535 
536 	~BlacklistMenuItem()
537 	{
538 		fNode->Release();
539 
540 		// make sure the submenu is destroyed
541 		SetSubmenu(fSubMenu);
542 	}
543 
544 	bool IsDirectoryItem() const
545 	{
546 		return fNode->Type() == S_IFDIR;
547 	}
548 
549 	bool GetPath(BlacklistedPath& _path) const
550 	{
551 		Menu* menu = Supermenu();
552 		if (menu != NULL && menu != sBlacklistRootMenu
553 			&& menu->Superitem() != NULL) {
554 			return static_cast<BlacklistMenuItem*>(menu->Superitem())
555 					->GetPath(_path)
556 			   && _path.Append(Label());
557 		}
558 
559 		return _path.SetTo(Label());
560 	}
561 
562 	void UpdateBlacklisted()
563 	{
564 		BlacklistedPath path;
565 		if (GetPath(path))
566 			_SetMarked(sPathBlacklist->Contains(path.Path()), false);
567 	}
568 
569 	virtual void SetMarked(bool marked)
570 	{
571 		_SetMarked(marked, true);
572 	}
573 
574 	static bool Less(const MenuItem* a, const MenuItem* b)
575 	{
576 		const BlacklistMenuItem* item1
577 			= static_cast<const BlacklistMenuItem*>(a);
578 		const BlacklistMenuItem* item2
579 			= static_cast<const BlacklistMenuItem*>(b);
580 
581 		// directories come first
582 		if (item1->IsDirectoryItem() != item2->IsDirectoryItem())
583 			return item1->IsDirectoryItem();
584 
585 		// compare the labels
586 		return strcasecmp(item1->Label(), item2->Label()) < 0;
587 	}
588 
589 private:
590 	void _SetMarked(bool marked, bool updateBlacklist)
591 	{
592 		if (marked == IsMarked())
593 			return;
594 
595 		// For directories toggle the availability of the submenu.
596 		if (IsDirectoryItem())
597 			SetSubmenu(marked ? NULL : fSubMenu);
598 
599 		if (updateBlacklist) {
600 			BlacklistedPath path;
601 			if (GetPath(path)) {
602 				if (marked)
603 					sPathBlacklist->Add(path.Path());
604 				else
605 					sPathBlacklist->Remove(path.Path());
606 			}
607 		}
608 
609 		MenuItem::SetMarked(marked);
610 	}
611 
612 private:
613 	Node*	fNode;
614 	Menu*	fSubMenu;
615 };
616 
617 
618 class BlacklistMenu : public Menu {
619 public:
620 	BlacklistMenu()
621 		:
622 		Menu(STANDARD_MENU, kDefaultMenuTitle),
623 		fDirectory(NULL)
624 	{
625 	}
626 
627 	~BlacklistMenu()
628 	{
629 		SetDirectory(NULL);
630 	}
631 
632 	virtual void Entered()
633 	{
634 		_DeleteItems();
635 
636 		if (fDirectory != NULL) {
637 			void* cookie;
638 			if (fDirectory->Open(&cookie, O_RDONLY) == B_OK) {
639 				Node* node;
640 				while (fDirectory->GetNextNode(cookie, &node) == B_OK) {
641 					BlacklistMenuItem* item = _CreateItem(node);
642 					node->Release();
643 					if (item == NULL)
644 						break;
645 
646 					AddItem(item);
647 
648 					item->UpdateBlacklisted();
649 				}
650 				fDirectory->Close(cookie);
651 			}
652 
653 			SortItems(&BlacklistMenuItem::Less);
654 		}
655 
656 		if (CountItems() > 0)
657 			AddSeparatorItem();
658 		AddItem(new(nothrow) MenuItem("Return to parent directory"));
659 	}
660 
661 	virtual void Exited()
662 	{
663 		_DeleteItems();
664 	}
665 
666 protected:
667 	void SetDirectory(Directory* directory)
668 	{
669 		if (fDirectory != NULL)
670 			fDirectory->Release();
671 
672 		fDirectory = directory;
673 
674 		if (fDirectory != NULL)
675 			fDirectory->Acquire();
676 	}
677 
678 private:
679 	static BlacklistMenuItem* _CreateItem(Node* node)
680 	{
681 		// Get the node name and duplicate it, so we can use it as a label.
682 		char name[B_FILE_NAME_LENGTH];
683 		if (node->GetName(name, sizeof(name)) != B_OK)
684 			return NULL;
685 
686 		// append '/' to directory labels
687 		bool isDirectory = node->Type() == S_IFDIR;
688 		if (isDirectory)
689 			strlcat(name, "/", sizeof(name));
690 
691 		// If this is a directory, create the submenu.
692 		BlacklistMenu* subMenu = NULL;
693 		if (isDirectory) {
694 			subMenu = new(std::nothrow) BlacklistMenu;
695 			if (subMenu != NULL)
696 				subMenu->SetDirectory(static_cast<Directory*>(node));
697 
698 		}
699 		ObjectDeleter<BlacklistMenu> subMenuDeleter(subMenu);
700 
701 		// create the menu item
702 		BlacklistMenuItem* item = new(std::nothrow) BlacklistMenuItem(name,
703 			node, subMenu);
704 		if (item == NULL)
705 			return NULL;
706 
707 		subMenuDeleter.Detach();
708 		return item;
709 	}
710 
711 	void _DeleteItems()
712 	{
713 		int32 count = CountItems();
714 		for (int32 i = 0; i < count; i++)
715 			delete RemoveItemAt(0);
716 	}
717 
718 private:
719 	Directory*	fDirectory;
720 
721 protected:
722 	static const char* const kDefaultMenuTitle;
723 };
724 
725 
726 const char* const BlacklistMenu::kDefaultMenuTitle
727 	= "Mark the entries to blacklist";
728 
729 
730 class BlacklistRootMenu : public BlacklistMenu {
731 public:
732 	BlacklistRootMenu()
733 		:
734 		BlacklistMenu()
735 	{
736 	}
737 
738 	virtual void Entered()
739 	{
740 		// Get the system directory, but only if this is a packaged Haiku.
741 		// Otherwise blacklisting isn't supported.
742 		if (sBootVolume != NULL && sBootVolume->IsValid()
743 			&& sBootVolume->IsPackaged()) {
744 			SetDirectory(sBootVolume->SystemDirectory());
745 			SetTitle(kDefaultMenuTitle);
746 		} else {
747 			SetDirectory(NULL);
748 			SetTitle(sBootVolume != NULL && sBootVolume->IsValid()
749 				? "The selected boot volume doesn't support blacklisting!"
750 				: "No boot volume selected!");
751 		}
752 
753 		BlacklistMenu::Entered();
754 
755 		// rename last item
756 		if (MenuItem* item = ItemAt(CountItems() - 1))
757 			item->SetLabel("Return to safe mode menu");
758 	}
759 
760 	virtual void Exited()
761 	{
762 		BlacklistMenu::Exited();
763 		SetDirectory(NULL);
764 	}
765 };
766 
767 
768 // #pragma mark - boot volume menu
769 
770 
771 class BootVolumeMenuItem : public MenuItem {
772 public:
773 	BootVolumeMenuItem(const char* volumeName)
774 		:
775 		MenuItem(volumeName),
776 		fStateChoiceText(NULL)
777 	{
778 	}
779 
780 	~BootVolumeMenuItem()
781 	{
782 		UpdateStateName(NULL);
783 	}
784 
785 	void UpdateStateName(PackageVolumeState* volumeState)
786 	{
787 		free(fStateChoiceText);
788 		fStateChoiceText = NULL;
789 
790 		if (volumeState != NULL && volumeState->Name() != NULL) {
791 			char nameBuffer[128];
792 			snprintf(nameBuffer, sizeof(nameBuffer), "%s (%s)", Label(),
793 				volumeState->DisplayName());
794 			fStateChoiceText = strdup(nameBuffer);
795 		}
796 
797 		Supermenu()->SetChoiceText(
798 			fStateChoiceText != NULL ? fStateChoiceText : Label());
799 	}
800 
801 private:
802 	char*	fStateChoiceText;
803 };
804 
805 
806 class PackageVolumeStateMenuItem : public MenuItem {
807 public:
808 	PackageVolumeStateMenuItem(const char* label, PackageVolumeInfo* volumeInfo,
809 		PackageVolumeState*	volumeState)
810 		:
811 		MenuItem(label),
812 		fVolumeInfo(volumeInfo),
813 		fVolumeState(volumeState)
814 	{
815 		fVolumeInfo->AcquireReference();
816 	}
817 
818 	~PackageVolumeStateMenuItem()
819 	{
820 		fVolumeInfo->ReleaseReference();
821 	}
822 
823 	PackageVolumeInfo* VolumeInfo() const
824 	{
825 		return fVolumeInfo;
826 	}
827 
828 	PackageVolumeState* VolumeState() const
829 	{
830 		return fVolumeState;
831 	}
832 
833 private:
834 	PackageVolumeInfo*	fVolumeInfo;
835 	PackageVolumeState*	fVolumeState;
836 };
837 
838 
839 // #pragma mark -
840 
841 
842 class StringBuffer {
843 public:
844 	StringBuffer()
845 		:
846 		fBuffer(NULL),
847 		fLength(0),
848 		fCapacity(0)
849 	{
850 	}
851 
852 	~StringBuffer()
853 	{
854 		free(fBuffer);
855 	}
856 
857 	const char* String() const
858 	{
859 		return fBuffer != NULL ? fBuffer : "";
860 	}
861 
862 	size_t Length() const
863 	{
864 		return fLength;
865 	}
866 
867 	bool Append(const char* toAppend)
868 	{
869 		return Append(toAppend, strlen(toAppend));
870 	}
871 
872 	bool Append(const char* toAppend, size_t length)
873 	{
874 		size_t oldLength = fLength;
875 		if (!_Resize(fLength + length))
876 			return false;
877 
878 		memcpy(fBuffer + oldLength, toAppend, length);
879 		return true;
880 	}
881 
882 private:
883 	bool _Resize(size_t newLength)
884 	{
885 		if (newLength >= fCapacity) {
886 			size_t newCapacity = std::max(fCapacity, size_t(32));
887 			while (newLength >= newCapacity)
888 				newCapacity *= 2;
889 
890 			char* buffer = (char*)realloc(fBuffer, newCapacity);
891 			if (buffer == NULL)
892 				return false;
893 
894 			fBuffer = buffer;
895 			fCapacity = newCapacity;
896 		}
897 
898 		fBuffer[newLength] = '\0';
899 		fLength = newLength;
900 		return true;
901 	}
902 
903 private:
904 	char*	fBuffer;
905 	size_t	fLength;
906 	size_t	fCapacity;
907 };
908 
909 
910 // #pragma mark -
911 
912 
913 static StringBuffer sSafeModeOptionsBuffer;
914 
915 
916 static MenuItem*
917 get_continue_booting_menu_item()
918 {
919 	// It's the last item in the main menu.
920 	if (sMainMenu == NULL || sMainMenu->CountItems() == 0)
921 		return NULL;
922 	return sMainMenu->ItemAt(sMainMenu->CountItems() - 1);
923 }
924 
925 
926 static void
927 update_continue_booting_menu_item(status_t status)
928 {
929 	MenuItem* bootItem = get_continue_booting_menu_item();
930 	if (bootItem == NULL) {
931 		// huh?
932 		return;
933 	}
934 
935 	if (status == B_OK) {
936 		bootItem->SetLabel("Continue booting");
937 		bootItem->SetEnabled(true);
938 		bootItem->Select(true);
939 	} else {
940 		char label[128];
941 		snprintf(label, sizeof(label), "Cannot continue booting (%s)",
942 			strerror(status));
943 		bootItem->SetLabel(label);
944 		bootItem->SetEnabled(false);
945 	}
946 }
947 
948 
949 static bool
950 user_menu_boot_volume(Menu* menu, MenuItem* item)
951 {
952 	if (sBootVolume->IsValid() && sBootVolume->RootDirectory() == item->Data())
953 		return true;
954 
955 	sPathBlacklist->MakeEmpty();
956 
957 	status_t status = sBootVolume->SetTo((Directory*)item->Data());
958 	update_continue_booting_menu_item(status);
959 
960 	gBootVolume.SetBool(BOOT_VOLUME_USER_SELECTED, true);
961 	return true;
962 }
963 
964 
965 static bool
966 user_menu_boot_volume_state(Menu* menu, MenuItem* _item)
967 {
968 	PackageVolumeStateMenuItem* item = static_cast<PackageVolumeStateMenuItem*>(
969 		_item);
970 	if (sBootVolume->IsValid() && sBootVolume->GetPackageVolumeState() != NULL
971 			&& sBootVolume->GetPackageVolumeState() == item->VolumeState()) {
972 		return true;
973 	}
974 
975 	BootVolumeMenuItem* volumeItem = static_cast<BootVolumeMenuItem*>(
976 		item->Supermenu()->Superitem());
977 	volumeItem->SetMarked(true);
978 	volumeItem->Select(true);
979 	volumeItem->UpdateStateName(item->VolumeState());
980 
981 	sPathBlacklist->MakeEmpty();
982 
983 	status_t status = sBootVolume->SetTo((Directory*)item->Data(),
984 		item->VolumeInfo(), item->VolumeState());
985 	update_continue_booting_menu_item(status);
986 
987 	gBootVolume.SetBool(BOOT_VOLUME_USER_SELECTED, true);
988 	return true;
989 }
990 
991 
992 static bool
993 debug_menu_display_current_log(Menu* menu, MenuItem* item)
994 {
995 	// get the buffer
996 	size_t bufferSize;
997 	const char* buffer = platform_debug_get_log_buffer(&bufferSize);
998 	if (buffer == NULL || bufferSize == 0)
999 		return true;
1000 
1001 	struct TextSource : PagerTextSource {
1002 		TextSource(const char* buffer, size_t size)
1003 			:
1004 			fBuffer(buffer),
1005 			fSize(strnlen(buffer, size))
1006 		{
1007 		}
1008 
1009 		virtual size_t BytesAvailable() const
1010 		{
1011 			return fSize;
1012 		}
1013 
1014 		virtual size_t Read(size_t offset, void* buffer, size_t size) const
1015 		{
1016 			if (offset >= fSize)
1017 				return 0;
1018 
1019 			if (size > fSize - offset)
1020 				size = fSize - offset;
1021 
1022 			memcpy(buffer, fBuffer + offset, size);
1023 			return size;
1024 		}
1025 
1026 	private:
1027 		const char*	fBuffer;
1028 		size_t		fSize;
1029 	};
1030 
1031 	pager(TextSource(buffer, bufferSize));
1032 
1033 	return true;
1034 }
1035 
1036 
1037 static bool
1038 debug_menu_display_previous_syslog(Menu* menu, MenuItem* item)
1039 {
1040 	ring_buffer* buffer = (ring_buffer*)gKernelArgs.debug_output.Pointer();
1041 	if (buffer == NULL)
1042 		return true;
1043 
1044 	struct TextSource : PagerTextSource {
1045 		TextSource(ring_buffer* buffer)
1046 			:
1047 			fBuffer(buffer)
1048 		{
1049 		}
1050 
1051 		virtual size_t BytesAvailable() const
1052 		{
1053 			return ring_buffer_readable(fBuffer);
1054 		}
1055 
1056 		virtual size_t Read(size_t offset, void* buffer, size_t size) const
1057 		{
1058 			return ring_buffer_peek(fBuffer, offset, buffer, size);
1059 		}
1060 
1061 	private:
1062 		ring_buffer*	fBuffer;
1063 	};
1064 
1065 	pager(TextSource(buffer));
1066 
1067 	return true;
1068 }
1069 
1070 
1071 static status_t
1072 save_previous_syslog_to_volume(Directory* directory)
1073 {
1074 	// find an unused name
1075 	char name[16];
1076 	bool found = false;
1077 	for (int i = 0; i < 99; i++) {
1078 		snprintf(name, sizeof(name), "SYSLOG%02d.TXT", i);
1079 		Node* node = directory->Lookup(name, false);
1080 		if (node == NULL) {
1081 			found = true;
1082 			break;
1083 		}
1084 
1085 		node->Release();
1086 	}
1087 
1088 	if (!found) {
1089 		printf("Failed to find an unused name for the syslog file!\n");
1090 		return B_ERROR;
1091 	}
1092 
1093 	printf("Writing syslog to file \"%s\" ...\n", name);
1094 
1095 	int fd = open_from(directory, name, O_RDWR | O_CREAT | O_EXCL, 0644);
1096 	if (fd < 0) {
1097 		printf("Failed to create syslog file!\n");
1098 		return fd;
1099 	}
1100 
1101 	ring_buffer* syslogBuffer
1102 		= (ring_buffer*)gKernelArgs.debug_output.Pointer();
1103 	iovec vecs[2];
1104 	int32 vecCount = ring_buffer_get_vecs(syslogBuffer, vecs);
1105 	if (vecCount > 0) {
1106 		size_t toWrite = ring_buffer_readable(syslogBuffer);
1107 
1108 		ssize_t written = writev(fd, vecs, vecCount);
1109 		if (written < 0 || (size_t)written != toWrite) {
1110 			printf("Failed to write to the syslog file \"%s\"!\n", name);
1111 			close(fd);
1112 			return errno;
1113 		}
1114 	}
1115 
1116 	close(fd);
1117 
1118 	printf("Successfully wrote syslog file.\n");
1119 
1120 	return B_OK;
1121 }
1122 
1123 
1124 static bool
1125 debug_menu_add_advanced_option(Menu* menu, MenuItem* item)
1126 {
1127 	char buffer[256];
1128 
1129 	size_t size = platform_get_user_input_text(menu, item, buffer,
1130 		sizeof(buffer) - 1);
1131 
1132 	if (size > 0) {
1133 		buffer[size] = '\n';
1134 		if (!sSafeModeOptionsBuffer.Append(buffer)) {
1135 			dprintf("debug_menu_add_advanced_option(): failed to append option "
1136 				"to buffer\n");
1137 		}
1138 	}
1139 
1140 	return true;
1141 }
1142 
1143 
1144 static bool
1145 debug_menu_toggle_debug_syslog(Menu* menu, MenuItem* item)
1146 {
1147 	gKernelArgs.keep_debug_output_buffer = item->IsMarked();
1148 	return true;
1149 }
1150 
1151 
1152 static bool
1153 debug_menu_toggle_previous_debug_syslog(Menu* menu, MenuItem* item)
1154 {
1155 	gKernelArgs.previous_debug_size = item->IsMarked();
1156 	return true;
1157 }
1158 
1159 
1160 static bool
1161 debug_menu_save_previous_syslog(Menu* menu, MenuItem* item)
1162 {
1163 	Directory* volume = (Directory*)item->Data();
1164 
1165 	console_clear_screen();
1166 
1167 	save_previous_syslog_to_volume(volume);
1168 
1169 	printf("\nPress any key to continue\n");
1170 	console_wait_for_key();
1171 
1172 	return true;
1173 }
1174 
1175 
1176 static void
1177 add_boot_volume_item(Menu* menu, Directory* volume, const char* name)
1178 {
1179 	BReference<PackageVolumeInfo> volumeInfo;
1180 	PackageVolumeState* selectedState = NULL;
1181 	if (volume == sBootVolume->RootDirectory()) {
1182 		volumeInfo.SetTo(sBootVolume->GetPackageVolumeInfo());
1183 		selectedState = sBootVolume->GetPackageVolumeState();
1184 	} else {
1185 		volumeInfo.SetTo(new(std::nothrow) PackageVolumeInfo);
1186 		if (volumeInfo->SetTo(volume, "system/packages") == B_OK)
1187 			selectedState = volumeInfo->States().Head();
1188 		else
1189 			volumeInfo.Unset();
1190 	}
1191 
1192 	BootVolumeMenuItem* item = new(nothrow) BootVolumeMenuItem(name);
1193 	menu->AddItem(item);
1194 
1195 	Menu* subMenu = NULL;
1196 
1197 	if (volumeInfo != NULL && volumeInfo->LoadOldStates() == B_OK) {
1198 		subMenu = new(std::nothrow) Menu(CHOICE_MENU, "Select Haiku version");
1199 
1200 		for (PackageVolumeStateList::ConstIterator it
1201 				= volumeInfo->States().GetIterator();
1202 			PackageVolumeState* state = it.Next();) {
1203 			PackageVolumeStateMenuItem* stateItem
1204 				= new(nothrow) PackageVolumeStateMenuItem(state->DisplayName(),
1205 					volumeInfo, state);
1206 			subMenu->AddItem(stateItem);
1207 			stateItem->SetTarget(user_menu_boot_volume_state);
1208 			stateItem->SetData(volume);
1209 
1210 			if (state == selectedState) {
1211 				stateItem->SetMarked(true);
1212 				stateItem->Select(true);
1213 				item->UpdateStateName(stateItem->VolumeState());
1214 			}
1215 		}
1216 	}
1217 
1218 	if (subMenu != NULL && subMenu->CountItems() > 1) {
1219 		item->SetHelpText(
1220 			"Enter to choose a different state to boot");
1221 		item->SetSubmenu(subMenu);
1222 	} else {
1223 		delete subMenu;
1224 		item->SetTarget(user_menu_boot_volume);
1225 		item->SetData(volume);
1226 	}
1227 
1228 	if (volume == sBootVolume->RootDirectory()) {
1229 		item->SetMarked(true);
1230 		item->Select(true);
1231 	}
1232 }
1233 
1234 
1235 static Menu*
1236 add_boot_volume_menu()
1237 {
1238 	Menu* menu = new(std::nothrow) Menu(CHOICE_MENU, "Select Boot Volume");
1239 	MenuItem* item;
1240 	void* cookie;
1241 	int32 count = 0;
1242 
1243 	if (gRoot->Open(&cookie, O_RDONLY) == B_OK) {
1244 		Directory* volume;
1245 		while (gRoot->GetNextNode(cookie, (Node**)&volume) == B_OK) {
1246 			// only list bootable volumes
1247 			if (volume != sBootVolume->RootDirectory() && !is_bootable(volume))
1248 				continue;
1249 
1250 			char name[B_FILE_NAME_LENGTH];
1251 			if (volume->GetName(name, sizeof(name)) == B_OK) {
1252 				add_boot_volume_item(menu, volume, name);
1253 
1254 				count++;
1255 			}
1256 		}
1257 		gRoot->Close(cookie);
1258 	}
1259 
1260 	if (count == 0) {
1261 		// no boot volume found yet
1262 		menu->AddItem(item = new(nothrow) MenuItem("<No boot volume found>"));
1263 		item->SetType(MENU_ITEM_NO_CHOICE);
1264 		item->SetEnabled(false);
1265 	}
1266 
1267 	menu->AddSeparatorItem();
1268 
1269 	menu->AddItem(item = new(nothrow) MenuItem("Rescan volumes"));
1270 	item->SetHelpText("Please insert a Haiku CD-ROM or attach a USB disk - "
1271 		"depending on your system, you can then boot from there.");
1272 	item->SetType(MENU_ITEM_NO_CHOICE);
1273 	if (count == 0)
1274 		item->Select(true);
1275 
1276 	menu->AddItem(item = new(nothrow) MenuItem("Return to main menu"));
1277 	item->SetType(MENU_ITEM_NO_CHOICE);
1278 
1279 	if (gBootVolume.GetBool(BOOT_VOLUME_BOOTED_FROM_IMAGE, false))
1280 		menu->SetChoiceText("CD-ROM or hard drive");
1281 
1282 	return menu;
1283 }
1284 
1285 
1286 static Menu*
1287 add_safe_mode_menu()
1288 {
1289 	Menu* safeMenu = new(nothrow) Menu(SAFE_MODE_MENU, "Safe Mode Options");
1290 	MenuItem* item;
1291 
1292 	safeMenu->AddItem(item = new(nothrow) MenuItem("Safe mode"));
1293 	item->SetData(B_SAFEMODE_SAFE_MODE);
1294 	item->SetType(MENU_ITEM_MARKABLE);
1295 	item->SetHelpText("Puts the system into safe mode. This can be enabled "
1296 		"independently from the other options.");
1297 
1298 	safeMenu->AddItem(item = new(nothrow) MenuItem("Disable user add-ons"));
1299 	item->SetData(B_SAFEMODE_DISABLE_USER_ADD_ONS);
1300 	item->SetType(MENU_ITEM_MARKABLE);
1301     item->SetHelpText("Prevents all user installed add-ons from being loaded. "
1302 		"Only the add-ons in the system directory will be used.");
1303 
1304 	safeMenu->AddItem(item = new(nothrow) MenuItem("Disable IDE DMA"));
1305 	item->SetData(B_SAFEMODE_DISABLE_IDE_DMA);
1306 	item->SetType(MENU_ITEM_MARKABLE);
1307     item->SetHelpText("Disables IDE DMA, increasing IDE compatibility "
1308 		"at the expense of performance.");
1309 
1310 #if B_HAIKU_PHYSICAL_BITS > 32
1311 	// check whether we have memory beyond 4 GB
1312 	bool hasMemoryBeyond4GB = false;
1313 	for (uint32 i = 0; i < gKernelArgs.num_physical_memory_ranges; i++) {
1314 		addr_range& range = gKernelArgs.physical_memory_range[i];
1315 		if (range.start >= (uint64)1 << 32) {
1316 			hasMemoryBeyond4GB = true;
1317 			break;
1318 		}
1319 	}
1320 
1321 	bool needs64BitPaging = true;
1322 		// TODO: Determine whether 64 bit paging (i.e. PAE for x86) is needed
1323 		// for other reasons (NX support).
1324 
1325 	// ... add the menu item, if so
1326 	if (hasMemoryBeyond4GB || needs64BitPaging) {
1327 		safeMenu->AddItem(
1328 			item = new(nothrow) MenuItem("Ignore memory beyond 4 GiB"));
1329 		item->SetData(B_SAFEMODE_4_GB_MEMORY_LIMIT);
1330 		item->SetType(MENU_ITEM_MARKABLE);
1331 		item->SetHelpText("Ignores all memory beyond the 4 GiB address limit, "
1332 			"overriding the setting in the kernel settings file.");
1333 	}
1334 #endif
1335 
1336 	platform_add_menus(safeMenu);
1337 
1338 	safeMenu->AddSeparatorItem();
1339 	sBlacklistRootMenu = new(std::nothrow) BlacklistRootMenu;
1340 	safeMenu->AddItem(item = new(std::nothrow) MenuItem("Blacklist entries",
1341 		sBlacklistRootMenu));
1342 	item->SetHelpText("Allows to select system files that shall be ignored. "
1343 		"Useful e.g. to disable drivers temporarily.");
1344 
1345 	safeMenu->AddSeparatorItem();
1346 	safeMenu->AddItem(item = new(nothrow) MenuItem("Return to main menu"));
1347 
1348 	return safeMenu;
1349 }
1350 
1351 
1352 static Menu*
1353 add_save_debug_syslog_menu()
1354 {
1355 	Menu* menu = new(nothrow) Menu(STANDARD_MENU, "Save syslog to volume ...");
1356 	MenuItem* item;
1357 
1358 	const char* const kHelpText = "Currently only FAT32 volumes are supported. "
1359 		"Newly plugged in removable devices are only recognized after "
1360 		"rebooting.";
1361 
1362 	int32 itemsAdded = 0;
1363 
1364 	void* cookie;
1365 	if (gRoot->Open(&cookie, O_RDONLY) == B_OK) {
1366 		Node* node;
1367 		while (gRoot->GetNextNode(cookie, &node) == B_OK) {
1368 			Directory* volume = static_cast<Directory*>(node);
1369 			Partition* partition;
1370 			if (gRoot->GetPartitionFor(volume, &partition) != B_OK)
1371 				continue;
1372 
1373 			// we support only FAT32 volumes ATM
1374 			if (partition->content_type == NULL
1375 				|| strcmp(partition->content_type, kPartitionTypeFAT32) != 0) {
1376 				continue;
1377 			}
1378 
1379 			char name[B_FILE_NAME_LENGTH];
1380 			if (volume->GetName(name, sizeof(name)) != B_OK)
1381 				strlcpy(name, "unnamed", sizeof(name));
1382 
1383 			// append offset, size, and type to the name
1384 			size_t len = strlen(name);
1385 			char offsetBuffer[32];
1386 			char sizeBuffer[32];
1387 			snprintf(name + len, sizeof(name) - len,
1388 				" (%s, offset %s, size %s)", partition->content_type,
1389 				size_to_string(partition->offset, offsetBuffer,
1390 					sizeof(offsetBuffer)),
1391 				size_to_string(partition->size, sizeBuffer,
1392 					sizeof(sizeBuffer)));
1393 
1394 			item = new(nothrow) MenuItem(name);
1395 			item->SetData(volume);
1396 			item->SetTarget(&debug_menu_save_previous_syslog);
1397 			item->SetType(MENU_ITEM_NO_CHOICE);
1398 			item->SetHelpText(kHelpText);
1399 			menu->AddItem(item);
1400 			itemsAdded++;
1401 		}
1402 
1403 		gRoot->Close(cookie);
1404 	}
1405 
1406 	if (itemsAdded == 0) {
1407 		menu->AddItem(item
1408 			= new(nothrow) MenuItem("No supported volumes found"));
1409 		item->SetType(MENU_ITEM_NO_CHOICE);
1410 		item->SetHelpText(kHelpText);
1411 		item->SetEnabled(false);
1412 	}
1413 
1414 	menu->AddSeparatorItem();
1415 	menu->AddItem(item = new(nothrow) MenuItem("Return to debug menu"));
1416 	item->SetHelpText(kHelpText);
1417 
1418 	return menu;
1419 }
1420 
1421 
1422 static Menu*
1423 add_debug_menu()
1424 {
1425 	Menu* menu = new(std::nothrow) Menu(STANDARD_MENU, "Debug Options");
1426 	MenuItem* item;
1427 
1428 #if DEBUG_SPINLOCK_LATENCIES
1429 	item = new(std::nothrow) MenuItem("Disable latency checks");
1430 	if (item != NULL) {
1431 		item->SetType(MENU_ITEM_MARKABLE);
1432 		item->SetData(B_SAFEMODE_DISABLE_LATENCY_CHECK);
1433 		item->SetHelpText("Disables latency check panics.");
1434 		menu->AddItem(item);
1435 	}
1436 #endif
1437 
1438 	menu->AddItem(item
1439 		= new(nothrow) MenuItem("Enable serial debug output"));
1440 	item->SetData("serial_debug_output");
1441 	item->SetType(MENU_ITEM_MARKABLE);
1442 	item->SetHelpText("Turns on forwarding the syslog output to the serial "
1443 		"interface (default: 115200, 8N1).");
1444 
1445 	menu->AddItem(item
1446 		= new(nothrow) MenuItem("Enable on screen debug output"));
1447 	item->SetData("debug_screen");
1448 	item->SetType(MENU_ITEM_MARKABLE);
1449 	item->SetHelpText("Displays debug output on screen while the system "
1450 		"is booting, instead of the normal boot logo.");
1451 
1452 	menu->AddItem(item
1453 		= new(nothrow) MenuItem("Disable on screen paging"));
1454 	item->SetData("disable_onscreen_paging");
1455 	item->SetType(MENU_ITEM_MARKABLE);
1456 	item->SetHelpText("Disables paging when on screen debug output is "
1457 		"enabled.");
1458 
1459 	menu->AddItem(item = new(nothrow) MenuItem("Enable debug syslog"));
1460 	item->SetType(MENU_ITEM_MARKABLE);
1461 	item->SetMarked(gKernelArgs.keep_debug_output_buffer);
1462 	item->SetTarget(&debug_menu_toggle_debug_syslog);
1463     item->SetHelpText("Enables a special in-memory syslog buffer for this "
1464     	"session that the boot loader will be able to access after rebooting.");
1465 
1466 	ring_buffer* syslogBuffer
1467 		= (ring_buffer*)gKernelArgs.debug_output.Pointer();
1468 	bool hasPreviousSyslog
1469 		= syslogBuffer != NULL && ring_buffer_readable(syslogBuffer) > 0;
1470 	if (hasPreviousSyslog) {
1471 		menu->AddItem(item = new(nothrow) MenuItem(
1472 			"Save syslog from previous session during boot"));
1473 		item->SetType(MENU_ITEM_MARKABLE);
1474 		item->SetMarked(gKernelArgs.previous_debug_size);
1475 		item->SetTarget(&debug_menu_toggle_previous_debug_syslog);
1476 		item->SetHelpText("Saves the syslog from the previous Haiku session to "
1477 			"/var/log/previous_syslog when booting.");
1478 	}
1479 
1480 	bool currentLogItemVisible = platform_debug_get_log_buffer(NULL) != NULL;
1481 	if (currentLogItemVisible) {
1482 		menu->AddSeparatorItem();
1483 		menu->AddItem(item
1484 			= new(nothrow) MenuItem("Display current boot loader log"));
1485 		item->SetTarget(&debug_menu_display_current_log);
1486 		item->SetType(MENU_ITEM_NO_CHOICE);
1487 		item->SetHelpText(
1488 			"Displays the debug info the boot loader has logged.");
1489 	}
1490 
1491 	if (hasPreviousSyslog) {
1492 		if (!currentLogItemVisible)
1493 			menu->AddSeparatorItem();
1494 
1495 		menu->AddItem(item
1496 			= new(nothrow) MenuItem("Display syslog from previous session"));
1497 		item->SetTarget(&debug_menu_display_previous_syslog);
1498 		item->SetType(MENU_ITEM_NO_CHOICE);
1499 		item->SetHelpText(
1500 			"Displays the syslog from the previous Haiku session.");
1501 
1502 		menu->AddItem(item = new(nothrow) MenuItem(
1503 			"Save syslog from previous session", add_save_debug_syslog_menu()));
1504 		item->SetHelpText("Saves the syslog from the previous Haiku session to "
1505 			"disk. Currently only FAT32 volumes are supported.");
1506 	}
1507 
1508 	menu->AddSeparatorItem();
1509 	menu->AddItem(item = new(nothrow) MenuItem(
1510 		"Add advanced debug option"));
1511 	item->SetType(MENU_ITEM_NO_CHOICE);
1512 	item->SetTarget(&debug_menu_add_advanced_option);
1513 	item->SetHelpText(
1514 		"Allows advanced debugging options to be entered directly.");
1515 
1516 	menu->AddSeparatorItem();
1517 	menu->AddItem(item = new(nothrow) MenuItem("Return to main menu"));
1518 
1519 	return menu;
1520 }
1521 
1522 
1523 static void
1524 apply_safe_mode_options(Menu* menu)
1525 {
1526 	MenuItemIterator iterator = menu->ItemIterator();
1527 	while (MenuItem* item = iterator.Next()) {
1528 		if (item->Type() == MENU_ITEM_SEPARATOR || !item->IsMarked()
1529 			|| item->Data() == NULL) {
1530 			continue;
1531 		}
1532 
1533 		char buffer[256];
1534 		if (snprintf(buffer, sizeof(buffer), "%s true\n",
1535 				(const char*)item->Data()) >= (int)sizeof(buffer)
1536 			|| !sSafeModeOptionsBuffer.Append(buffer)) {
1537 			dprintf("apply_safe_mode_options(): failed to append option to "
1538 				"buffer\n");
1539 		}
1540 	}
1541 }
1542 
1543 
1544 static void
1545 apply_safe_mode_path_blacklist()
1546 {
1547 	if (sPathBlacklist->IsEmpty())
1548 		return;
1549 
1550 	bool success = sSafeModeOptionsBuffer.Append("EntryBlacklist {\n");
1551 
1552 	for (PathBlacklist::Iterator it = sPathBlacklist->GetIterator();
1553 		BlacklistedPath* path = it.Next();) {
1554 		success &= sSafeModeOptionsBuffer.Append(path->Path());
1555 		success &= sSafeModeOptionsBuffer.Append("\n", 1);
1556 	}
1557 
1558 	success &= sSafeModeOptionsBuffer.Append("}\n");
1559 
1560 	if (!success) {
1561 		dprintf("apply_safe_mode_options(): failed to append path "
1562 			"blacklist to buffer\n");
1563 	}
1564 }
1565 
1566 
1567 static bool
1568 user_menu_reboot(Menu* menu, MenuItem* item)
1569 {
1570 	platform_exit();
1571 	return true;
1572 }
1573 
1574 
1575 status_t
1576 user_menu(BootVolume& _bootVolume, PathBlacklist& _pathBlacklist)
1577 {
1578 
1579 	Menu* menu = new(std::nothrow) Menu(MAIN_MENU);
1580 
1581 	sMainMenu = menu;
1582 	sBootVolume = &_bootVolume;
1583 	sPathBlacklist = &_pathBlacklist;
1584 
1585 	Menu* safeModeMenu = NULL;
1586 	Menu* debugMenu = NULL;
1587 	MenuItem* item;
1588 
1589 	TRACE(("user_menu: enter\n"));
1590 
1591 	// Add boot volume
1592 	menu->AddItem(item = new(std::nothrow) MenuItem("Select boot volume",
1593 		add_boot_volume_menu()));
1594 
1595 	// Add safe mode
1596 	menu->AddItem(item = new(std::nothrow) MenuItem("Select safe mode options",
1597 		safeModeMenu = add_safe_mode_menu()));
1598 
1599 	// add debug menu
1600 	menu->AddItem(item = new(std::nothrow) MenuItem("Select debug options",
1601 		debugMenu = add_debug_menu()));
1602 
1603 	// Add platform dependent menus
1604 	platform_add_menus(menu);
1605 
1606 	menu->AddSeparatorItem();
1607 
1608 	menu->AddItem(item = new(std::nothrow) MenuItem("Reboot"));
1609 	item->SetTarget(user_menu_reboot);
1610 	item->SetShortcut('r');
1611 
1612 	menu->AddItem(item = new(std::nothrow) MenuItem("Continue booting"));
1613 	if (!_bootVolume.IsValid()) {
1614 		item->SetLabel("Cannot continue booting (Boot volume is not valid)");
1615 		item->SetEnabled(false);
1616 		menu->ItemAt(0)->Select(true);
1617 	} else
1618 		item->SetShortcut('b');
1619 
1620 	menu->Run();
1621 
1622 	apply_safe_mode_options(safeModeMenu);
1623 	apply_safe_mode_options(debugMenu);
1624 	apply_safe_mode_path_blacklist();
1625 	add_safe_mode_settings(sSafeModeOptionsBuffer.String());
1626 	delete menu;
1627 
1628 
1629 	TRACE(("user_menu: leave\n"));
1630 
1631 	sMainMenu = NULL;
1632 	sBlacklistRootMenu = NULL;
1633 	sBootVolume = NULL;
1634 	sPathBlacklist = NULL;
1635 	return B_OK;
1636 }
1637