xref: /haiku/src/system/boot/loader/menu.cpp (revision 1e60bdeab63fa7a57bc9a55b032052e95a18bd2c)
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 bool
927 user_menu_boot_volume(Menu* menu, MenuItem* item)
928 {
929 	MenuItem* bootItem = get_continue_booting_menu_item();
930 	if (bootItem == NULL) {
931 		// huh?
932 		return true;
933 	}
934 
935 	if (sBootVolume->IsValid() && sBootVolume->RootDirectory() == item->Data())
936 		return true;
937 
938 	sPathBlacklist->MakeEmpty();
939 
940 	bool valid = sBootVolume->SetTo((Directory*)item->Data()) == B_OK;
941 
942 	bootItem->SetEnabled(valid);
943 	if (valid)
944 		bootItem->Select(true);
945 
946 	gBootVolume.SetBool(BOOT_VOLUME_USER_SELECTED, true);
947 	return true;
948 }
949 
950 
951 static bool
952 user_menu_boot_volume_state(Menu* menu, MenuItem* _item)
953 {
954 	MenuItem* bootItem = get_continue_booting_menu_item();
955 	if (bootItem == NULL) {
956 		// huh?
957 		return true;
958 	}
959 
960 	PackageVolumeStateMenuItem* item = static_cast<PackageVolumeStateMenuItem*>(
961 		_item);
962 	if (sBootVolume->IsValid() && sBootVolume->GetPackageVolumeState() != NULL
963 			&& sBootVolume->GetPackageVolumeState() == item->VolumeState()) {
964 		return true;
965 	}
966 
967 	BootVolumeMenuItem* volumeItem = static_cast<BootVolumeMenuItem*>(
968 		item->Supermenu()->Superitem());
969 	volumeItem->SetMarked(true);
970 	volumeItem->Select(true);
971 	volumeItem->UpdateStateName(item->VolumeState());
972 
973 	sPathBlacklist->MakeEmpty();
974 
975 	bool valid = sBootVolume->SetTo((Directory*)item->Data(),
976 		item->VolumeInfo(), item->VolumeState()) == B_OK;
977 
978 	bootItem->SetEnabled(valid);
979 	if (valid)
980 		bootItem->Select(true);
981 
982 	gBootVolume.SetBool(BOOT_VOLUME_USER_SELECTED, true);
983 	return true;
984 }
985 
986 
987 static bool
988 debug_menu_display_current_log(Menu* menu, MenuItem* item)
989 {
990 	// get the buffer
991 	size_t bufferSize;
992 	const char* buffer = platform_debug_get_log_buffer(&bufferSize);
993 	if (buffer == NULL || bufferSize == 0)
994 		return true;
995 
996 	struct TextSource : PagerTextSource {
997 		TextSource(const char* buffer, size_t size)
998 			:
999 			fBuffer(buffer),
1000 			fSize(strnlen(buffer, size))
1001 		{
1002 		}
1003 
1004 		virtual size_t BytesAvailable() const
1005 		{
1006 			return fSize;
1007 		}
1008 
1009 		virtual size_t Read(size_t offset, void* buffer, size_t size) const
1010 		{
1011 			if (offset >= fSize)
1012 				return 0;
1013 
1014 			if (size > fSize - offset)
1015 				size = fSize - offset;
1016 
1017 			memcpy(buffer, fBuffer + offset, size);
1018 			return size;
1019 		}
1020 
1021 	private:
1022 		const char*	fBuffer;
1023 		size_t		fSize;
1024 	};
1025 
1026 	pager(TextSource(buffer, bufferSize));
1027 
1028 	return true;
1029 }
1030 
1031 
1032 static bool
1033 debug_menu_display_previous_syslog(Menu* menu, MenuItem* item)
1034 {
1035 	ring_buffer* buffer = (ring_buffer*)gKernelArgs.debug_output.Pointer();
1036 	if (buffer == NULL)
1037 		return true;
1038 
1039 	struct TextSource : PagerTextSource {
1040 		TextSource(ring_buffer* buffer)
1041 			:
1042 			fBuffer(buffer)
1043 		{
1044 		}
1045 
1046 		virtual size_t BytesAvailable() const
1047 		{
1048 			return ring_buffer_readable(fBuffer);
1049 		}
1050 
1051 		virtual size_t Read(size_t offset, void* buffer, size_t size) const
1052 		{
1053 			return ring_buffer_peek(fBuffer, offset, buffer, size);
1054 		}
1055 
1056 	private:
1057 		ring_buffer*	fBuffer;
1058 	};
1059 
1060 	pager(TextSource(buffer));
1061 
1062 	return true;
1063 }
1064 
1065 
1066 static status_t
1067 save_previous_syslog_to_volume(Directory* directory)
1068 {
1069 	// find an unused name
1070 	char name[16];
1071 	bool found = false;
1072 	for (int i = 0; i < 99; i++) {
1073 		snprintf(name, sizeof(name), "SYSLOG%02d.TXT", i);
1074 		Node* node = directory->Lookup(name, false);
1075 		if (node == NULL) {
1076 			found = true;
1077 			break;
1078 		}
1079 
1080 		node->Release();
1081 	}
1082 
1083 	if (!found) {
1084 		printf("Failed to find an unused name for the syslog file!\n");
1085 		return B_ERROR;
1086 	}
1087 
1088 	printf("Writing syslog to file \"%s\" ...\n", name);
1089 
1090 	int fd = open_from(directory, name, O_RDWR | O_CREAT | O_EXCL, 0644);
1091 	if (fd < 0) {
1092 		printf("Failed to create syslog file!\n");
1093 		return fd;
1094 	}
1095 
1096 	ring_buffer* syslogBuffer
1097 		= (ring_buffer*)gKernelArgs.debug_output.Pointer();
1098 	iovec vecs[2];
1099 	int32 vecCount = ring_buffer_get_vecs(syslogBuffer, vecs);
1100 	if (vecCount > 0) {
1101 		size_t toWrite = ring_buffer_readable(syslogBuffer);
1102 
1103 		ssize_t written = writev(fd, vecs, vecCount);
1104 		if (written < 0 || (size_t)written != toWrite) {
1105 			printf("Failed to write to the syslog file \"%s\"!\n", name);
1106 			close(fd);
1107 			return errno;
1108 		}
1109 	}
1110 
1111 	close(fd);
1112 
1113 	printf("Successfully wrote syslog file.\n");
1114 
1115 	return B_OK;
1116 }
1117 
1118 
1119 static bool
1120 debug_menu_add_advanced_option(Menu* menu, MenuItem* item)
1121 {
1122 	char buffer[256];
1123 
1124 	size_t size = platform_get_user_input_text(menu, item, buffer,
1125 		sizeof(buffer) - 1);
1126 
1127 	if (size > 0) {
1128 		buffer[size] = '\n';
1129 		if (!sSafeModeOptionsBuffer.Append(buffer)) {
1130 			dprintf("debug_menu_add_advanced_option(): failed to append option "
1131 				"to buffer\n");
1132 		}
1133 	}
1134 
1135 	return true;
1136 }
1137 
1138 
1139 static bool
1140 debug_menu_toggle_debug_syslog(Menu* menu, MenuItem* item)
1141 {
1142 	gKernelArgs.keep_debug_output_buffer = item->IsMarked();
1143 	return true;
1144 }
1145 
1146 
1147 static bool
1148 debug_menu_toggle_previous_debug_syslog(Menu* menu, MenuItem* item)
1149 {
1150 	gKernelArgs.previous_debug_size = item->IsMarked();
1151 	return true;
1152 }
1153 
1154 
1155 static bool
1156 debug_menu_save_previous_syslog(Menu* menu, MenuItem* item)
1157 {
1158 	Directory* volume = (Directory*)item->Data();
1159 
1160 	console_clear_screen();
1161 
1162 	save_previous_syslog_to_volume(volume);
1163 
1164 	printf("\nPress any key to continue\n");
1165 	console_wait_for_key();
1166 
1167 	return true;
1168 }
1169 
1170 
1171 static void
1172 add_boot_volume_item(Menu* menu, Directory* volume, const char* name)
1173 {
1174 	BReference<PackageVolumeInfo> volumeInfo;
1175 	PackageVolumeState* selectedState = NULL;
1176 	if (volume == sBootVolume->RootDirectory()) {
1177 		volumeInfo.SetTo(sBootVolume->GetPackageVolumeInfo());
1178 		selectedState = sBootVolume->GetPackageVolumeState();
1179 	} else {
1180 		volumeInfo.SetTo(new(std::nothrow) PackageVolumeInfo);
1181 		if (volumeInfo->SetTo(volume, "system/packages") == B_OK)
1182 			selectedState = volumeInfo->States().Head();
1183 		else
1184 			volumeInfo.Unset();
1185 	}
1186 
1187 	BootVolumeMenuItem* item = new(nothrow) BootVolumeMenuItem(name);
1188 	menu->AddItem(item);
1189 
1190 	Menu* subMenu = NULL;
1191 
1192 	if (volumeInfo != NULL) {
1193 		subMenu = new(std::nothrow) Menu(CHOICE_MENU, "Select Haiku version");
1194 
1195 		for (PackageVolumeStateList::ConstIterator it
1196 				= volumeInfo->States().GetIterator();
1197 			PackageVolumeState* state = it.Next();) {
1198 			PackageVolumeStateMenuItem* stateItem
1199 				= new(nothrow) PackageVolumeStateMenuItem(state->DisplayName(),
1200 					volumeInfo, state);
1201 			subMenu->AddItem(stateItem);
1202 			stateItem->SetTarget(user_menu_boot_volume_state);
1203 			stateItem->SetData(volume);
1204 
1205 			if (state == selectedState) {
1206 				stateItem->SetMarked(true);
1207 				stateItem->Select(true);
1208 				item->UpdateStateName(stateItem->VolumeState());
1209 			}
1210 		}
1211 	}
1212 
1213 	if (subMenu != NULL && subMenu->CountItems() > 1) {
1214 		item->SetHelpText(
1215 			"Enter to choose a different state to boot");
1216 		item->SetSubmenu(subMenu);
1217 	} else {
1218 		delete subMenu;
1219 		item->SetTarget(user_menu_boot_volume);
1220 		item->SetData(volume);
1221 	}
1222 
1223 	if (volume == sBootVolume->RootDirectory()) {
1224 		item->SetMarked(true);
1225 		item->Select(true);
1226 	}
1227 }
1228 
1229 
1230 static Menu*
1231 add_boot_volume_menu()
1232 {
1233 	Menu* menu = new(std::nothrow) Menu(CHOICE_MENU, "Select Boot Volume");
1234 	MenuItem* item;
1235 	void* cookie;
1236 	int32 count = 0;
1237 
1238 	if (gRoot->Open(&cookie, O_RDONLY) == B_OK) {
1239 		Directory* volume;
1240 		while (gRoot->GetNextNode(cookie, (Node**)&volume) == B_OK) {
1241 			// only list bootable volumes
1242 			if (volume != sBootVolume->RootDirectory() && !is_bootable(volume))
1243 				continue;
1244 
1245 			char name[B_FILE_NAME_LENGTH];
1246 			if (volume->GetName(name, sizeof(name)) == B_OK) {
1247 				add_boot_volume_item(menu, volume, name);
1248 
1249 				count++;
1250 			}
1251 		}
1252 		gRoot->Close(cookie);
1253 	}
1254 
1255 	if (count == 0) {
1256 		// no boot volume found yet
1257 		menu->AddItem(item = new(nothrow) MenuItem("<No boot volume found>"));
1258 		item->SetType(MENU_ITEM_NO_CHOICE);
1259 		item->SetEnabled(false);
1260 	}
1261 
1262 	menu->AddSeparatorItem();
1263 
1264 	menu->AddItem(item = new(nothrow) MenuItem("Rescan volumes"));
1265 	item->SetHelpText("Please insert a Haiku CD-ROM or attach a USB disk - "
1266 		"depending on your system, you can then boot from there.");
1267 	item->SetType(MENU_ITEM_NO_CHOICE);
1268 	if (count == 0)
1269 		item->Select(true);
1270 
1271 	menu->AddItem(item = new(nothrow) MenuItem("Return to main menu"));
1272 	item->SetType(MENU_ITEM_NO_CHOICE);
1273 
1274 	if (gBootVolume.GetBool(BOOT_VOLUME_BOOTED_FROM_IMAGE, false))
1275 		menu->SetChoiceText("CD-ROM or hard drive");
1276 
1277 	return menu;
1278 }
1279 
1280 
1281 static Menu*
1282 add_safe_mode_menu()
1283 {
1284 	Menu* safeMenu = new(nothrow) Menu(SAFE_MODE_MENU, "Safe Mode Options");
1285 	MenuItem* item;
1286 
1287 	safeMenu->AddItem(item = new(nothrow) MenuItem("Safe mode"));
1288 	item->SetData(B_SAFEMODE_SAFE_MODE);
1289 	item->SetType(MENU_ITEM_MARKABLE);
1290 	item->SetHelpText("Puts the system into safe mode. This can be enabled "
1291 		"independently from the other options.");
1292 
1293 	safeMenu->AddItem(item = new(nothrow) MenuItem("Disable user add-ons"));
1294 	item->SetData(B_SAFEMODE_DISABLE_USER_ADD_ONS);
1295 	item->SetType(MENU_ITEM_MARKABLE);
1296     item->SetHelpText("Prevents all user installed add-ons from being loaded. "
1297 		"Only the add-ons in the system directory will be used.");
1298 
1299 	safeMenu->AddItem(item = new(nothrow) MenuItem("Disable IDE DMA"));
1300 	item->SetData(B_SAFEMODE_DISABLE_IDE_DMA);
1301 	item->SetType(MENU_ITEM_MARKABLE);
1302     item->SetHelpText("Disables IDE DMA, increasing IDE compatibility "
1303 		"at the expense of performance.");
1304 
1305 #if B_HAIKU_PHYSICAL_BITS > 32
1306 	// check whether we have memory beyond 4 GB
1307 	bool hasMemoryBeyond4GB = false;
1308 	for (uint32 i = 0; i < gKernelArgs.num_physical_memory_ranges; i++) {
1309 		addr_range& range = gKernelArgs.physical_memory_range[i];
1310 		if (range.start >= (uint64)1 << 32) {
1311 			hasMemoryBeyond4GB = true;
1312 			break;
1313 		}
1314 	}
1315 
1316 	bool needs64BitPaging = true;
1317 		// TODO: Determine whether 64 bit paging (i.e. PAE for x86) is needed
1318 		// for other reasons (NX support).
1319 
1320 	// ... add the menu item, if so
1321 	if (hasMemoryBeyond4GB || needs64BitPaging) {
1322 		safeMenu->AddItem(
1323 			item = new(nothrow) MenuItem("Ignore memory beyond 4 GiB"));
1324 		item->SetData(B_SAFEMODE_4_GB_MEMORY_LIMIT);
1325 		item->SetType(MENU_ITEM_MARKABLE);
1326 		item->SetHelpText("Ignores all memory beyond the 4 GiB address limit, "
1327 			"overriding the setting in the kernel settings file.");
1328 	}
1329 #endif
1330 
1331 	platform_add_menus(safeMenu);
1332 
1333 	safeMenu->AddSeparatorItem();
1334 	sBlacklistRootMenu = new(std::nothrow) BlacklistRootMenu;
1335 	safeMenu->AddItem(item = new(std::nothrow) MenuItem("Blacklist entries",
1336 		sBlacklistRootMenu));
1337 	item->SetHelpText("Allows to select system files that shall be ignored. "
1338 		"Useful e.g. to disable drivers temporarily.");
1339 
1340 	safeMenu->AddSeparatorItem();
1341 	safeMenu->AddItem(item = new(nothrow) MenuItem("Return to main menu"));
1342 
1343 	return safeMenu;
1344 }
1345 
1346 
1347 static Menu*
1348 add_save_debug_syslog_menu()
1349 {
1350 	Menu* menu = new(nothrow) Menu(STANDARD_MENU, "Save syslog to volume ...");
1351 	MenuItem* item;
1352 
1353 	const char* const kHelpText = "Currently only FAT32 volumes are supported. "
1354 		"Newly plugged in removable devices are only recognized after "
1355 		"rebooting.";
1356 
1357 	int32 itemsAdded = 0;
1358 
1359 	void* cookie;
1360 	if (gRoot->Open(&cookie, O_RDONLY) == B_OK) {
1361 		Node* node;
1362 		while (gRoot->GetNextNode(cookie, &node) == B_OK) {
1363 			Directory* volume = static_cast<Directory*>(node);
1364 			Partition* partition;
1365 			if (gRoot->GetPartitionFor(volume, &partition) != B_OK)
1366 				continue;
1367 
1368 			// we support only FAT32 volumes ATM
1369 			if (partition->content_type == NULL
1370 				|| strcmp(partition->content_type, kPartitionTypeFAT32) != 0) {
1371 				continue;
1372 			}
1373 
1374 			char name[B_FILE_NAME_LENGTH];
1375 			if (volume->GetName(name, sizeof(name)) != B_OK)
1376 				strlcpy(name, "unnamed", sizeof(name));
1377 
1378 			// append offset, size, and type to the name
1379 			size_t len = strlen(name);
1380 			char offsetBuffer[32];
1381 			char sizeBuffer[32];
1382 			snprintf(name + len, sizeof(name) - len,
1383 				" (%s, offset %s, size %s)", partition->content_type,
1384 				size_to_string(partition->offset, offsetBuffer,
1385 					sizeof(offsetBuffer)),
1386 				size_to_string(partition->size, sizeBuffer,
1387 					sizeof(sizeBuffer)));
1388 
1389 			item = new(nothrow) MenuItem(name);
1390 			item->SetData(volume);
1391 			item->SetTarget(&debug_menu_save_previous_syslog);
1392 			item->SetType(MENU_ITEM_NO_CHOICE);
1393 			item->SetHelpText(kHelpText);
1394 			menu->AddItem(item);
1395 			itemsAdded++;
1396 		}
1397 
1398 		gRoot->Close(cookie);
1399 	}
1400 
1401 	if (itemsAdded == 0) {
1402 		menu->AddItem(item
1403 			= new(nothrow) MenuItem("No supported volumes found"));
1404 		item->SetType(MENU_ITEM_NO_CHOICE);
1405 		item->SetHelpText(kHelpText);
1406 		item->SetEnabled(false);
1407 	}
1408 
1409 	menu->AddSeparatorItem();
1410 	menu->AddItem(item = new(nothrow) MenuItem("Return to debug menu"));
1411 	item->SetHelpText(kHelpText);
1412 
1413 	return menu;
1414 }
1415 
1416 
1417 static Menu*
1418 add_debug_menu()
1419 {
1420 	Menu* menu = new(std::nothrow) Menu(STANDARD_MENU, "Debug Options");
1421 	MenuItem* item;
1422 
1423 #if DEBUG_SPINLOCK_LATENCIES
1424 	item = new(std::nothrow) MenuItem("Disable latency checks");
1425 	if (item != NULL) {
1426 		item->SetType(MENU_ITEM_MARKABLE);
1427 		item->SetData(B_SAFEMODE_DISABLE_LATENCY_CHECK);
1428 		item->SetHelpText("Disables latency check panics.");
1429 		menu->AddItem(item);
1430 	}
1431 #endif
1432 
1433 	menu->AddItem(item
1434 		= new(nothrow) MenuItem("Enable serial debug output"));
1435 	item->SetData("serial_debug_output");
1436 	item->SetType(MENU_ITEM_MARKABLE);
1437 	item->SetHelpText("Turns on forwarding the syslog output to the serial "
1438 		"interface (default: 115200, 8N1).");
1439 
1440 	menu->AddItem(item
1441 		= new(nothrow) MenuItem("Enable on screen debug output"));
1442 	item->SetData("debug_screen");
1443 	item->SetType(MENU_ITEM_MARKABLE);
1444 	item->SetHelpText("Displays debug output on screen while the system "
1445 		"is booting, instead of the normal boot logo.");
1446 
1447 	menu->AddItem(item
1448 		= new(nothrow) MenuItem("Disable on screen paging"));
1449 	item->SetData("disable_onscreen_paging");
1450 	item->SetType(MENU_ITEM_MARKABLE);
1451 	item->SetHelpText("Disables paging when on screen debug output is "
1452 		"enabled.");
1453 
1454 	menu->AddItem(item = new(nothrow) MenuItem("Enable debug syslog"));
1455 	item->SetType(MENU_ITEM_MARKABLE);
1456 	item->SetMarked(gKernelArgs.keep_debug_output_buffer);
1457 	item->SetTarget(&debug_menu_toggle_debug_syslog);
1458     item->SetHelpText("Enables a special in-memory syslog buffer for this "
1459     	"session that the boot loader will be able to access after rebooting.");
1460 
1461 	ring_buffer* syslogBuffer
1462 		= (ring_buffer*)gKernelArgs.debug_output.Pointer();
1463 	bool hasPreviousSyslog
1464 		= syslogBuffer != NULL && ring_buffer_readable(syslogBuffer) > 0;
1465 	if (hasPreviousSyslog) {
1466 		menu->AddItem(item = new(nothrow) MenuItem(
1467 			"Save syslog from previous session during boot"));
1468 		item->SetType(MENU_ITEM_MARKABLE);
1469 		item->SetMarked(gKernelArgs.previous_debug_size);
1470 		item->SetTarget(&debug_menu_toggle_previous_debug_syslog);
1471 		item->SetHelpText("Saves the syslog from the previous Haiku session to "
1472 			"/var/log/previous_syslog when booting.");
1473 	}
1474 
1475 	bool currentLogItemVisible = platform_debug_get_log_buffer(NULL) != NULL;
1476 	if (currentLogItemVisible) {
1477 		menu->AddSeparatorItem();
1478 		menu->AddItem(item
1479 			= new(nothrow) MenuItem("Display current boot loader log"));
1480 		item->SetTarget(&debug_menu_display_current_log);
1481 		item->SetType(MENU_ITEM_NO_CHOICE);
1482 		item->SetHelpText(
1483 			"Displays the debug info the boot loader has logged.");
1484 	}
1485 
1486 	if (hasPreviousSyslog) {
1487 		if (!currentLogItemVisible)
1488 			menu->AddSeparatorItem();
1489 
1490 		menu->AddItem(item
1491 			= new(nothrow) MenuItem("Display syslog from previous session"));
1492 		item->SetTarget(&debug_menu_display_previous_syslog);
1493 		item->SetType(MENU_ITEM_NO_CHOICE);
1494 		item->SetHelpText(
1495 			"Displays the syslog from the previous Haiku session.");
1496 
1497 		menu->AddItem(item = new(nothrow) MenuItem(
1498 			"Save syslog from previous session", add_save_debug_syslog_menu()));
1499 		item->SetHelpText("Saves the syslog from the previous Haiku session to "
1500 			"disk. Currently only FAT32 volumes are supported.");
1501 	}
1502 
1503 	menu->AddSeparatorItem();
1504 	menu->AddItem(item = new(nothrow) MenuItem(
1505 		"Add advanced debug option"));
1506 	item->SetType(MENU_ITEM_NO_CHOICE);
1507 	item->SetTarget(&debug_menu_add_advanced_option);
1508 	item->SetHelpText(
1509 		"Allows advanced debugging options to be entered directly.");
1510 
1511 	menu->AddSeparatorItem();
1512 	menu->AddItem(item = new(nothrow) MenuItem("Return to main menu"));
1513 
1514 	return menu;
1515 }
1516 
1517 
1518 static void
1519 apply_safe_mode_options(Menu* menu)
1520 {
1521 	MenuItemIterator iterator = menu->ItemIterator();
1522 	while (MenuItem* item = iterator.Next()) {
1523 		if (item->Type() == MENU_ITEM_SEPARATOR || !item->IsMarked()
1524 			|| item->Data() == NULL) {
1525 			continue;
1526 		}
1527 
1528 		char buffer[256];
1529 		if (snprintf(buffer, sizeof(buffer), "%s true\n",
1530 				(const char*)item->Data()) >= (int)sizeof(buffer)
1531 			|| !sSafeModeOptionsBuffer.Append(buffer)) {
1532 			dprintf("apply_safe_mode_options(): failed to append option to "
1533 				"buffer\n");
1534 		}
1535 	}
1536 }
1537 
1538 
1539 static void
1540 apply_safe_mode_path_blacklist()
1541 {
1542 	if (sPathBlacklist->IsEmpty())
1543 		return;
1544 
1545 	bool success = sSafeModeOptionsBuffer.Append("EntryBlacklist {\n");
1546 
1547 	for (PathBlacklist::Iterator it = sPathBlacklist->GetIterator();
1548 		BlacklistedPath* path = it.Next();) {
1549 		success &= sSafeModeOptionsBuffer.Append(path->Path());
1550 		success &= sSafeModeOptionsBuffer.Append("\n", 1);
1551 	}
1552 
1553 	success &= sSafeModeOptionsBuffer.Append("}\n");
1554 
1555 	if (!success) {
1556 		dprintf("apply_safe_mode_options(): failed to append path "
1557 			"blacklist to buffer\n");
1558 	}
1559 }
1560 
1561 
1562 static bool
1563 user_menu_reboot(Menu* menu, MenuItem* item)
1564 {
1565 	platform_exit();
1566 	return true;
1567 }
1568 
1569 
1570 status_t
1571 user_menu(BootVolume& _bootVolume, PathBlacklist& _pathBlacklist)
1572 {
1573 
1574 	Menu* menu = new(std::nothrow) Menu(MAIN_MENU);
1575 
1576 	sMainMenu = menu;
1577 	sBootVolume = &_bootVolume;
1578 	sPathBlacklist = &_pathBlacklist;
1579 
1580 	Menu* safeModeMenu = NULL;
1581 	Menu* debugMenu = NULL;
1582 	MenuItem* item;
1583 
1584 	TRACE(("user_menu: enter\n"));
1585 
1586 	// Add boot volume
1587 	menu->AddItem(item = new(std::nothrow) MenuItem("Select boot volume",
1588 		add_boot_volume_menu()));
1589 
1590 	// Add safe mode
1591 	menu->AddItem(item = new(std::nothrow) MenuItem("Select safe mode options",
1592 		safeModeMenu = add_safe_mode_menu()));
1593 
1594 	// add debug menu
1595 	menu->AddItem(item = new(std::nothrow) MenuItem("Select debug options",
1596 		debugMenu = add_debug_menu()));
1597 
1598 	// Add platform dependent menus
1599 	platform_add_menus(menu);
1600 
1601 	menu->AddSeparatorItem();
1602 
1603 	menu->AddItem(item = new(std::nothrow) MenuItem("Reboot"));
1604 	item->SetTarget(user_menu_reboot);
1605 	item->SetShortcut('r');
1606 
1607 	menu->AddItem(item = new(std::nothrow) MenuItem("Continue booting"));
1608 	if (!_bootVolume.IsValid()) {
1609 		item->SetEnabled(false);
1610 		menu->ItemAt(0)->Select(true);
1611 	} else
1612 		item->SetShortcut('b');
1613 
1614 	menu->Run();
1615 
1616 	apply_safe_mode_options(safeModeMenu);
1617 	apply_safe_mode_options(debugMenu);
1618 	apply_safe_mode_path_blacklist();
1619 	add_safe_mode_settings(sSafeModeOptionsBuffer.String());
1620 	delete menu;
1621 
1622 
1623 	TRACE(("user_menu: leave\n"));
1624 
1625 	sMainMenu = NULL;
1626 	sBlacklistRootMenu = NULL;
1627 	sBootVolume = NULL;
1628 	sPathBlacklist = NULL;
1629 	return B_OK;
1630 }
1631