xref: /haiku/src/system/boot/loader/menu.cpp (revision e1c4049fed1047bdb957b0529e1921e97ef94770)
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/PathBlocklist.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* sBlocklistRootMenu = NULL;
50 static BootVolume* sBootVolume = NULL;
51 static PathBlocklist* sPathBlocklist;
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 - blocklist menu
522 
523 
524 class BlocklistMenuItem : public MenuItem {
525 public:
526 	BlocklistMenuItem(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 	~BlocklistMenuItem()
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(BlockedPath& _path) const
550 	{
551 		Menu* menu = Supermenu();
552 		if (menu != NULL && menu != sBlocklistRootMenu
553 			&& menu->Superitem() != NULL) {
554 			return static_cast<BlocklistMenuItem*>(menu->Superitem())
555 					->GetPath(_path)
556 			   && _path.Append(Label());
557 		}
558 
559 		return _path.SetTo(Label());
560 	}
561 
562 	void UpdateBlocked()
563 	{
564 		BlockedPath path;
565 		if (GetPath(path))
566 			_SetMarked(sPathBlocklist->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 BlocklistMenuItem* item1
577 			= static_cast<const BlocklistMenuItem*>(a);
578 		const BlocklistMenuItem* item2
579 			= static_cast<const BlocklistMenuItem*>(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 updateBlocklist)
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 (updateBlocklist) {
600 			BlockedPath path;
601 			if (GetPath(path)) {
602 				if (marked)
603 					sPathBlocklist->Add(path.Path());
604 				else
605 					sPathBlocklist->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 BlocklistMenu : public Menu {
619 public:
620 	BlocklistMenu()
621 		:
622 		Menu(STANDARD_MENU, kDefaultMenuTitle),
623 		fDirectory(NULL)
624 	{
625 	}
626 
627 	~BlocklistMenu()
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 					BlocklistMenuItem* item = _CreateItem(node);
642 					node->Release();
643 					if (item == NULL)
644 						break;
645 
646 					AddItem(item);
647 
648 					item->UpdateBlocked();
649 				}
650 				fDirectory->Close(cookie);
651 			}
652 
653 			SortItems(&BlocklistMenuItem::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 BlocklistMenuItem* _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 		BlocklistMenu* subMenu = NULL;
693 		if (isDirectory) {
694 			subMenu = new(std::nothrow) BlocklistMenu;
695 			if (subMenu != NULL)
696 				subMenu->SetDirectory(static_cast<Directory*>(node));
697 
698 		}
699 		ObjectDeleter<BlocklistMenu> subMenuDeleter(subMenu);
700 
701 		// create the menu item
702 		BlocklistMenuItem* item = new(std::nothrow) BlocklistMenuItem(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 BlocklistMenu::kDefaultMenuTitle
727 	= "Mark the components to disable";
728 
729 
730 class BlocklistRootMenu : public BlocklistMenu {
731 public:
732 	BlocklistRootMenu()
733 		:
734 		BlocklistMenu()
735 	{
736 	}
737 
738 	virtual void Entered()
739 	{
740 		// Get the system directory, but only if this is a packaged Haiku.
741 		// Otherwise blocklisting 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 disabling "
750 				  "components!"
751 				: "No boot volume selected!");
752 		}
753 
754 		BlocklistMenu::Entered();
755 
756 		// rename last item
757 		if (MenuItem* item = ItemAt(CountItems() - 1))
758 			item->SetLabel("Return to safe mode menu");
759 	}
760 
761 	virtual void Exited()
762 	{
763 		BlocklistMenu::Exited();
764 		SetDirectory(NULL);
765 	}
766 };
767 
768 
769 // #pragma mark - boot volume menu
770 
771 
772 class BootVolumeMenuItem : public MenuItem {
773 public:
774 	BootVolumeMenuItem(const char* volumeName)
775 		:
776 		MenuItem(volumeName),
777 		fStateChoiceText(NULL)
778 	{
779 	}
780 
781 	~BootVolumeMenuItem()
782 	{
783 		UpdateStateName(NULL);
784 	}
785 
786 	void UpdateStateName(PackageVolumeState* volumeState)
787 	{
788 		free(fStateChoiceText);
789 		fStateChoiceText = NULL;
790 
791 		if (volumeState != NULL && volumeState->Name() != NULL) {
792 			char nameBuffer[128];
793 			snprintf(nameBuffer, sizeof(nameBuffer), "%s (%s)", Label(),
794 				volumeState->DisplayName());
795 			fStateChoiceText = strdup(nameBuffer);
796 		}
797 
798 		Supermenu()->SetChoiceText(
799 			fStateChoiceText != NULL ? fStateChoiceText : Label());
800 	}
801 
802 private:
803 	char*	fStateChoiceText;
804 };
805 
806 
807 class PackageVolumeStateMenuItem : public MenuItem {
808 public:
809 	PackageVolumeStateMenuItem(const char* label, PackageVolumeInfo* volumeInfo,
810 		PackageVolumeState*	volumeState)
811 		:
812 		MenuItem(label),
813 		fVolumeInfo(volumeInfo),
814 		fVolumeState(volumeState)
815 	{
816 		fVolumeInfo->AcquireReference();
817 	}
818 
819 	~PackageVolumeStateMenuItem()
820 	{
821 		fVolumeInfo->ReleaseReference();
822 	}
823 
824 	PackageVolumeInfo* VolumeInfo() const
825 	{
826 		return fVolumeInfo;
827 	}
828 
829 	PackageVolumeState* VolumeState() const
830 	{
831 		return fVolumeState;
832 	}
833 
834 private:
835 	PackageVolumeInfo*	fVolumeInfo;
836 	PackageVolumeState*	fVolumeState;
837 };
838 
839 
840 // #pragma mark -
841 
842 
843 class StringBuffer {
844 public:
845 	StringBuffer()
846 		:
847 		fBuffer(NULL),
848 		fLength(0),
849 		fCapacity(0)
850 	{
851 	}
852 
853 	~StringBuffer()
854 	{
855 		free(fBuffer);
856 	}
857 
858 	const char* String() const
859 	{
860 		return fBuffer != NULL ? fBuffer : "";
861 	}
862 
863 	size_t Length() const
864 	{
865 		return fLength;
866 	}
867 
868 	bool Append(const char* toAppend)
869 	{
870 		return Append(toAppend, strlen(toAppend));
871 	}
872 
873 	bool Append(const char* toAppend, size_t length)
874 	{
875 		size_t oldLength = fLength;
876 		if (!_Resize(fLength + length))
877 			return false;
878 
879 		memcpy(fBuffer + oldLength, toAppend, length);
880 		return true;
881 	}
882 
883 private:
884 	bool _Resize(size_t newLength)
885 	{
886 		if (newLength >= fCapacity) {
887 			size_t newCapacity = std::max(fCapacity, size_t(32));
888 			while (newLength >= newCapacity)
889 				newCapacity *= 2;
890 
891 			char* buffer = (char*)realloc(fBuffer, newCapacity);
892 			if (buffer == NULL)
893 				return false;
894 
895 			fBuffer = buffer;
896 			fCapacity = newCapacity;
897 		}
898 
899 		fBuffer[newLength] = '\0';
900 		fLength = newLength;
901 		return true;
902 	}
903 
904 private:
905 	char*	fBuffer;
906 	size_t	fLength;
907 	size_t	fCapacity;
908 };
909 
910 
911 // #pragma mark -
912 
913 
914 static StringBuffer sSafeModeOptionsBuffer;
915 
916 
917 static MenuItem*
918 get_continue_booting_menu_item()
919 {
920 	// It's the last item in the main menu.
921 	if (sMainMenu == NULL || sMainMenu->CountItems() == 0)
922 		return NULL;
923 	return sMainMenu->ItemAt(sMainMenu->CountItems() - 1);
924 }
925 
926 
927 static void
928 update_continue_booting_menu_item(status_t status)
929 {
930 	MenuItem* bootItem = get_continue_booting_menu_item();
931 	if (bootItem == NULL) {
932 		// huh?
933 		return;
934 	}
935 
936 	if (status == B_OK) {
937 		bootItem->SetLabel("Continue booting");
938 		bootItem->SetEnabled(true);
939 		bootItem->Select(true);
940 	} else {
941 		char label[128];
942 		snprintf(label, sizeof(label), "Cannot continue booting (%s)",
943 			strerror(status));
944 		bootItem->SetLabel(label);
945 		bootItem->SetEnabled(false);
946 	}
947 }
948 
949 
950 static bool
951 user_menu_boot_volume(Menu* menu, MenuItem* item)
952 {
953 	if (sBootVolume->IsValid() && sBootVolume->RootDirectory() == item->Data())
954 		return true;
955 
956 	sPathBlocklist->MakeEmpty();
957 
958 	status_t status = sBootVolume->SetTo((Directory*)item->Data());
959 	update_continue_booting_menu_item(status);
960 
961 	gBootVolume.SetBool(BOOT_VOLUME_USER_SELECTED, true);
962 	return true;
963 }
964 
965 
966 static bool
967 user_menu_boot_volume_state(Menu* menu, MenuItem* _item)
968 {
969 	PackageVolumeStateMenuItem* item = static_cast<PackageVolumeStateMenuItem*>(
970 		_item);
971 	if (sBootVolume->IsValid() && sBootVolume->GetPackageVolumeState() != NULL
972 			&& sBootVolume->GetPackageVolumeState() == item->VolumeState()) {
973 		return true;
974 	}
975 
976 	BootVolumeMenuItem* volumeItem = static_cast<BootVolumeMenuItem*>(
977 		item->Supermenu()->Superitem());
978 	volumeItem->SetMarked(true);
979 	volumeItem->Select(true);
980 	volumeItem->UpdateStateName(item->VolumeState());
981 
982 	sPathBlocklist->MakeEmpty();
983 
984 	status_t status = sBootVolume->SetTo((Directory*)item->Data(),
985 		item->VolumeInfo(), item->VolumeState());
986 	update_continue_booting_menu_item(status);
987 
988 	gBootVolume.SetBool(BOOT_VOLUME_USER_SELECTED, true);
989 	return true;
990 }
991 
992 
993 static bool
994 debug_menu_display_current_log(Menu* menu, MenuItem* item)
995 {
996 	// get the buffer
997 	size_t bufferSize;
998 	const char* buffer = platform_debug_get_log_buffer(&bufferSize);
999 	if (buffer == NULL || bufferSize == 0)
1000 		return true;
1001 
1002 	struct TextSource : PagerTextSource {
1003 		TextSource(const char* buffer, size_t size)
1004 			:
1005 			fBuffer(buffer),
1006 			fSize(strnlen(buffer, size))
1007 		{
1008 		}
1009 
1010 		virtual size_t BytesAvailable() const
1011 		{
1012 			return fSize;
1013 		}
1014 
1015 		virtual size_t Read(size_t offset, void* buffer, size_t size) const
1016 		{
1017 			if (offset >= fSize)
1018 				return 0;
1019 
1020 			if (size > fSize - offset)
1021 				size = fSize - offset;
1022 
1023 			memcpy(buffer, fBuffer + offset, size);
1024 			return size;
1025 		}
1026 
1027 	private:
1028 		const char*	fBuffer;
1029 		size_t		fSize;
1030 	};
1031 
1032 	pager(TextSource(buffer, bufferSize));
1033 
1034 	return true;
1035 }
1036 
1037 
1038 static bool
1039 debug_menu_display_previous_syslog(Menu* menu, MenuItem* item)
1040 {
1041 	ring_buffer* buffer = (ring_buffer*)gKernelArgs.debug_output.Pointer();
1042 	if (buffer == NULL)
1043 		return true;
1044 
1045 	struct TextSource : PagerTextSource {
1046 		TextSource(ring_buffer* buffer)
1047 			:
1048 			fBuffer(buffer)
1049 		{
1050 		}
1051 
1052 		virtual size_t BytesAvailable() const
1053 		{
1054 			return ring_buffer_readable(fBuffer);
1055 		}
1056 
1057 		virtual size_t Read(size_t offset, void* buffer, size_t size) const
1058 		{
1059 			return ring_buffer_peek(fBuffer, offset, buffer, size);
1060 		}
1061 
1062 	private:
1063 		ring_buffer*	fBuffer;
1064 	};
1065 
1066 	pager(TextSource(buffer));
1067 
1068 	return true;
1069 }
1070 
1071 
1072 static status_t
1073 save_previous_syslog_to_volume(Directory* directory)
1074 {
1075 	// find an unused name
1076 	char name[16];
1077 	bool found = false;
1078 	for (int i = 0; i < 99; i++) {
1079 		snprintf(name, sizeof(name), "SYSLOG%02d.TXT", i);
1080 		Node* node = directory->Lookup(name, false);
1081 		if (node == NULL) {
1082 			found = true;
1083 			break;
1084 		}
1085 
1086 		node->Release();
1087 	}
1088 
1089 	if (!found) {
1090 		printf("Failed to find an unused name for the syslog file!\n");
1091 		return B_ERROR;
1092 	}
1093 
1094 	printf("Writing syslog to file \"%s\" ...\n", name);
1095 
1096 	int fd = open_from(directory, name, O_RDWR | O_CREAT | O_EXCL, 0644);
1097 	if (fd < 0) {
1098 		printf("Failed to create syslog file!\n");
1099 		return fd;
1100 	}
1101 
1102 	ring_buffer* syslogBuffer
1103 		= (ring_buffer*)gKernelArgs.debug_output.Pointer();
1104 	iovec vecs[2];
1105 	int32 vecCount = ring_buffer_get_vecs(syslogBuffer, vecs);
1106 	if (vecCount > 0) {
1107 		size_t toWrite = ring_buffer_readable(syslogBuffer);
1108 
1109 		ssize_t written = writev(fd, vecs, vecCount);
1110 		if (written < 0 || (size_t)written != toWrite) {
1111 			printf("Failed to write to the syslog file \"%s\"!\n", name);
1112 			close(fd);
1113 			return errno;
1114 		}
1115 	}
1116 
1117 	close(fd);
1118 
1119 	printf("Successfully wrote syslog file.\n");
1120 
1121 	return B_OK;
1122 }
1123 
1124 
1125 static bool
1126 debug_menu_add_advanced_option(Menu* menu, MenuItem* item)
1127 {
1128 	char buffer[256];
1129 
1130 	size_t size = platform_get_user_input_text(menu, item, buffer,
1131 		sizeof(buffer) - 1);
1132 
1133 	if (size > 0) {
1134 		buffer[size] = '\n';
1135 		if (!sSafeModeOptionsBuffer.Append(buffer)) {
1136 			dprintf("debug_menu_add_advanced_option(): failed to append option "
1137 				"to buffer\n");
1138 		}
1139 	}
1140 
1141 	return true;
1142 }
1143 
1144 
1145 static bool
1146 debug_menu_toggle_debug_syslog(Menu* menu, MenuItem* item)
1147 {
1148 	gKernelArgs.keep_debug_output_buffer = item->IsMarked();
1149 	return true;
1150 }
1151 
1152 
1153 static bool
1154 debug_menu_toggle_previous_debug_syslog(Menu* menu, MenuItem* item)
1155 {
1156 	gKernelArgs.previous_debug_size = item->IsMarked();
1157 	return true;
1158 }
1159 
1160 
1161 static bool
1162 debug_menu_save_previous_syslog(Menu* menu, MenuItem* item)
1163 {
1164 	Directory* volume = (Directory*)item->Data();
1165 
1166 	console_clear_screen();
1167 
1168 	save_previous_syslog_to_volume(volume);
1169 
1170 	printf("\nPress any key to continue\n");
1171 	console_wait_for_key();
1172 
1173 	return true;
1174 }
1175 
1176 
1177 static void
1178 add_boot_volume_item(Menu* menu, Directory* volume, const char* name)
1179 {
1180 	BReference<PackageVolumeInfo> volumeInfo;
1181 	PackageVolumeState* selectedState = NULL;
1182 	if (volume == sBootVolume->RootDirectory()) {
1183 		volumeInfo.SetTo(sBootVolume->GetPackageVolumeInfo());
1184 		selectedState = sBootVolume->GetPackageVolumeState();
1185 	} else {
1186 		volumeInfo.SetTo(new(std::nothrow) PackageVolumeInfo);
1187 		if (volumeInfo->SetTo(volume, "system/packages") == B_OK)
1188 			selectedState = volumeInfo->States().Head();
1189 		else
1190 			volumeInfo.Unset();
1191 	}
1192 
1193 	BootVolumeMenuItem* item = new(nothrow) BootVolumeMenuItem(name);
1194 	menu->AddItem(item);
1195 
1196 	Menu* subMenu = NULL;
1197 
1198 	if (volumeInfo != NULL && volumeInfo->LoadOldStates() == B_OK) {
1199 		subMenu = new(std::nothrow) Menu(CHOICE_MENU, "Select package activation state");
1200 
1201 		for (PackageVolumeStateList::ConstIterator it
1202 				= volumeInfo->States().GetIterator();
1203 			PackageVolumeState* state = it.Next();) {
1204 			PackageVolumeStateMenuItem* stateItem
1205 				= new(nothrow) PackageVolumeStateMenuItem(state->DisplayName(),
1206 					volumeInfo, state);
1207 			subMenu->AddItem(stateItem);
1208 			stateItem->SetTarget(user_menu_boot_volume_state);
1209 			stateItem->SetData(volume);
1210 
1211 			if (state == selectedState) {
1212 				stateItem->SetMarked(true);
1213 				stateItem->Select(true);
1214 				if (volume == sBootVolume->RootDirectory()) {
1215 					item->UpdateStateName(stateItem->VolumeState());
1216 				}
1217 			}
1218 		}
1219 	}
1220 
1221 	if (subMenu != NULL && subMenu->CountItems() > 1) {
1222 		item->SetHelpText(
1223 			"Enter to choose a different state to boot");
1224 		item->SetSubmenu(subMenu);
1225 	} else {
1226 		delete subMenu;
1227 		item->SetTarget(user_menu_boot_volume);
1228 		item->SetData(volume);
1229 	}
1230 
1231 	if (volume == sBootVolume->RootDirectory()) {
1232 		item->SetMarked(true);
1233 		item->Select(true);
1234 	}
1235 }
1236 
1237 
1238 static Menu*
1239 add_boot_volume_menu()
1240 {
1241 	Menu* menu = new(std::nothrow) Menu(CHOICE_MENU, "Select Boot Volume/State");
1242 	MenuItem* item;
1243 	void* cookie;
1244 	int32 count = 0;
1245 
1246 	if (gRoot->Open(&cookie, O_RDONLY) == B_OK) {
1247 		Directory* volume;
1248 		while (gRoot->GetNextNode(cookie, (Node**)&volume) == B_OK) {
1249 			// only list bootable volumes
1250 			if (volume != sBootVolume->RootDirectory() && !is_bootable(volume))
1251 				continue;
1252 
1253 			char name[B_FILE_NAME_LENGTH];
1254 			if (volume->GetName(name, sizeof(name)) == B_OK) {
1255 				add_boot_volume_item(menu, volume, name);
1256 
1257 				count++;
1258 			}
1259 		}
1260 		gRoot->Close(cookie);
1261 	}
1262 
1263 	if (count == 0) {
1264 		// no boot volume found yet
1265 		menu->AddItem(item = new(nothrow) MenuItem("<No boot volume found>"));
1266 		item->SetType(MENU_ITEM_NO_CHOICE);
1267 		item->SetEnabled(false);
1268 	}
1269 
1270 	menu->AddSeparatorItem();
1271 
1272 	menu->AddItem(item = new(nothrow) MenuItem("Rescan volumes"));
1273 	item->SetHelpText("Please insert a Haiku CD-ROM or attach a USB disk - "
1274 		"depending on your system, you can then boot from there.");
1275 	item->SetType(MENU_ITEM_NO_CHOICE);
1276 	if (count == 0)
1277 		item->Select(true);
1278 
1279 	menu->AddItem(item = new(nothrow) MenuItem("Return to main menu"));
1280 	item->SetType(MENU_ITEM_NO_CHOICE);
1281 
1282 	if (gBootVolume.GetBool(BOOT_VOLUME_BOOTED_FROM_IMAGE, false))
1283 		menu->SetChoiceText("CD-ROM or hard drive");
1284 
1285 	return menu;
1286 }
1287 
1288 
1289 static Menu*
1290 add_safe_mode_menu()
1291 {
1292 	Menu* safeMenu = new(nothrow) Menu(SAFE_MODE_MENU, "Safe Mode Options");
1293 	MenuItem* item;
1294 
1295 	safeMenu->AddItem(item = new(nothrow) MenuItem("Safe mode"));
1296 	item->SetData(B_SAFEMODE_SAFE_MODE);
1297 	item->SetType(MENU_ITEM_MARKABLE);
1298 	item->SetHelpText("Puts the system into safe mode. This can be enabled "
1299 		"independently from the other options.");
1300 
1301 	safeMenu->AddItem(item = new(nothrow) MenuItem("Disable user add-ons"));
1302 	item->SetData(B_SAFEMODE_DISABLE_USER_ADD_ONS);
1303 	item->SetType(MENU_ITEM_MARKABLE);
1304     item->SetHelpText("Prevents all user installed add-ons from being loaded. "
1305 		"Only the add-ons in the system directory will be used.");
1306 
1307 	safeMenu->AddItem(item = new(nothrow) MenuItem("Disable IDE DMA"));
1308 	item->SetData(B_SAFEMODE_DISABLE_IDE_DMA);
1309 	item->SetType(MENU_ITEM_MARKABLE);
1310     item->SetHelpText("Disables IDE DMA, increasing IDE compatibility "
1311 		"at the expense of performance.");
1312 
1313 #if B_HAIKU_PHYSICAL_BITS > 32
1314 	// check whether we have memory beyond 4 GB
1315 	bool hasMemoryBeyond4GB = false;
1316 	for (uint32 i = 0; i < gKernelArgs.num_physical_memory_ranges; i++) {
1317 		addr_range& range = gKernelArgs.physical_memory_range[i];
1318 		if (range.start >= (uint64)1 << 32) {
1319 			hasMemoryBeyond4GB = true;
1320 			break;
1321 		}
1322 	}
1323 
1324 	bool needs64BitPaging = true;
1325 		// TODO: Determine whether 64 bit paging (i.e. PAE for x86) is needed
1326 		// for other reasons (NX support).
1327 
1328 	// ... add the menu item, if so
1329 	if (hasMemoryBeyond4GB || needs64BitPaging) {
1330 		safeMenu->AddItem(
1331 			item = new(nothrow) MenuItem("Ignore memory beyond 4 GiB"));
1332 		item->SetData(B_SAFEMODE_4_GB_MEMORY_LIMIT);
1333 		item->SetType(MENU_ITEM_MARKABLE);
1334 		item->SetHelpText("Ignores all memory beyond the 4 GiB address limit, "
1335 			"overriding the setting in the kernel settings file.");
1336 	}
1337 #endif
1338 
1339 	platform_add_menus(safeMenu);
1340 
1341 	safeMenu->AddSeparatorItem();
1342 	sBlocklistRootMenu = new(std::nothrow) BlocklistRootMenu;
1343 	safeMenu->AddItem(item = new(std::nothrow) MenuItem(
1344 		"Disable system components", sBlocklistRootMenu));
1345 	item->SetHelpText("Allows to select system files that shall be ignored. "
1346 		"Useful e.g. to disable drivers temporarily.");
1347 
1348 	safeMenu->AddSeparatorItem();
1349 	safeMenu->AddItem(item = new(nothrow) MenuItem("Return to main menu"));
1350 
1351 	return safeMenu;
1352 }
1353 
1354 
1355 static Menu*
1356 add_save_debug_syslog_menu()
1357 {
1358 	Menu* menu = new(nothrow) Menu(STANDARD_MENU, "Save syslog to volume ...");
1359 	MenuItem* item;
1360 
1361 	const char* const kHelpText = "Currently only FAT32 volumes are supported. "
1362 		"Newly plugged in removable devices are only recognized after "
1363 		"rebooting.";
1364 
1365 	int32 itemsAdded = 0;
1366 
1367 	void* cookie;
1368 	if (gRoot->Open(&cookie, O_RDONLY) == B_OK) {
1369 		Node* node;
1370 		while (gRoot->GetNextNode(cookie, &node) == B_OK) {
1371 			Directory* volume = static_cast<Directory*>(node);
1372 			Partition* partition;
1373 			if (gRoot->GetPartitionFor(volume, &partition) != B_OK)
1374 				continue;
1375 
1376 			// we support only FAT32 volumes ATM
1377 			if (partition->content_type == NULL
1378 				|| strcmp(partition->content_type, kPartitionTypeFAT32) != 0) {
1379 				continue;
1380 			}
1381 
1382 			char name[B_FILE_NAME_LENGTH];
1383 			if (volume->GetName(name, sizeof(name)) != B_OK)
1384 				strlcpy(name, "unnamed", sizeof(name));
1385 
1386 			// append offset, size, and type to the name
1387 			size_t len = strlen(name);
1388 			char offsetBuffer[32];
1389 			char sizeBuffer[32];
1390 			snprintf(name + len, sizeof(name) - len,
1391 				" (%s, offset %s, size %s)", partition->content_type,
1392 				size_to_string(partition->offset, offsetBuffer,
1393 					sizeof(offsetBuffer)),
1394 				size_to_string(partition->size, sizeBuffer,
1395 					sizeof(sizeBuffer)));
1396 
1397 			item = new(nothrow) MenuItem(name);
1398 			item->SetData(volume);
1399 			item->SetTarget(&debug_menu_save_previous_syslog);
1400 			item->SetType(MENU_ITEM_NO_CHOICE);
1401 			item->SetHelpText(kHelpText);
1402 			menu->AddItem(item);
1403 			itemsAdded++;
1404 		}
1405 
1406 		gRoot->Close(cookie);
1407 	}
1408 
1409 	if (itemsAdded == 0) {
1410 		menu->AddItem(item
1411 			= new(nothrow) MenuItem("No supported volumes found"));
1412 		item->SetType(MENU_ITEM_NO_CHOICE);
1413 		item->SetHelpText(kHelpText);
1414 		item->SetEnabled(false);
1415 	}
1416 
1417 	menu->AddSeparatorItem();
1418 	menu->AddItem(item = new(nothrow) MenuItem("Return to debug menu"));
1419 	item->SetHelpText(kHelpText);
1420 
1421 	return menu;
1422 }
1423 
1424 
1425 static Menu*
1426 add_debug_menu()
1427 {
1428 	Menu* menu = new(std::nothrow) Menu(STANDARD_MENU, "Debug Options");
1429 	MenuItem* item;
1430 
1431 	menu->AddItem(item
1432 		= new(nothrow) MenuItem("Enable serial debug output"));
1433 	item->SetData("serial_debug_output");
1434 	item->SetType(MENU_ITEM_MARKABLE);
1435 	item->SetHelpText("Turns on forwarding the syslog output to the serial "
1436 		"interface (default: 115200, 8N1).");
1437 
1438 	menu->AddItem(item
1439 		= new(nothrow) MenuItem("Enable on screen debug output"));
1440 	item->SetData("debug_screen");
1441 	item->SetType(MENU_ITEM_MARKABLE);
1442 	item->SetHelpText("Displays debug output on screen while the system "
1443 		"is booting, instead of the normal boot logo.");
1444 
1445 	menu->AddItem(item
1446 		= new(nothrow) MenuItem("Disable on screen paging"));
1447 	item->SetData("disable_onscreen_paging");
1448 	item->SetType(MENU_ITEM_MARKABLE);
1449 	item->SetHelpText("Disables paging when on screen debug output is "
1450 		"enabled.");
1451 
1452 	menu->AddItem(item = new(nothrow) MenuItem("Enable debug syslog"));
1453 	item->SetType(MENU_ITEM_MARKABLE);
1454 	item->SetMarked(gKernelArgs.keep_debug_output_buffer);
1455 	item->SetTarget(&debug_menu_toggle_debug_syslog);
1456     item->SetHelpText("Enables a special in-memory syslog buffer for this "
1457     	"session that the boot loader will be able to access after rebooting.");
1458 
1459 	ring_buffer* syslogBuffer
1460 		= (ring_buffer*)gKernelArgs.debug_output.Pointer();
1461 	bool hasPreviousSyslog
1462 		= syslogBuffer != NULL && ring_buffer_readable(syslogBuffer) > 0;
1463 	if (hasPreviousSyslog) {
1464 		menu->AddItem(item = new(nothrow) MenuItem(
1465 			"Save syslog from previous session during boot"));
1466 		item->SetType(MENU_ITEM_MARKABLE);
1467 		item->SetMarked(gKernelArgs.previous_debug_size);
1468 		item->SetTarget(&debug_menu_toggle_previous_debug_syslog);
1469 		item->SetHelpText("Saves the syslog from the previous Haiku session to "
1470 			"/var/log/previous_syslog when booting.");
1471 	}
1472 
1473 	bool currentLogItemVisible = platform_debug_get_log_buffer(NULL) != NULL;
1474 	if (currentLogItemVisible) {
1475 		menu->AddSeparatorItem();
1476 		menu->AddItem(item
1477 			= new(nothrow) MenuItem("Display current boot loader log"));
1478 		item->SetTarget(&debug_menu_display_current_log);
1479 		item->SetType(MENU_ITEM_NO_CHOICE);
1480 		item->SetHelpText(
1481 			"Displays the debug info the boot loader has logged.");
1482 	}
1483 
1484 	if (hasPreviousSyslog) {
1485 		if (!currentLogItemVisible)
1486 			menu->AddSeparatorItem();
1487 
1488 		menu->AddItem(item
1489 			= new(nothrow) MenuItem("Display syslog from previous session"));
1490 		item->SetTarget(&debug_menu_display_previous_syslog);
1491 		item->SetType(MENU_ITEM_NO_CHOICE);
1492 		item->SetHelpText(
1493 			"Displays the syslog from the previous Haiku session.");
1494 
1495 		menu->AddItem(item = new(nothrow) MenuItem(
1496 			"Save syslog from previous session", add_save_debug_syslog_menu()));
1497 		item->SetHelpText("Saves the syslog from the previous Haiku session to "
1498 			"disk. Currently only FAT32 volumes are supported.");
1499 	}
1500 
1501 	menu->AddSeparatorItem();
1502 	menu->AddItem(item = new(nothrow) MenuItem(
1503 		"Add advanced debug option"));
1504 	item->SetType(MENU_ITEM_NO_CHOICE);
1505 	item->SetTarget(&debug_menu_add_advanced_option);
1506 	item->SetHelpText(
1507 		"Allows advanced debugging options to be entered directly.");
1508 
1509 	menu->AddSeparatorItem();
1510 	menu->AddItem(item = new(nothrow) MenuItem("Return to main menu"));
1511 
1512 	return menu;
1513 }
1514 
1515 
1516 static void
1517 apply_safe_mode_options(Menu* menu)
1518 {
1519 	MenuItemIterator iterator = menu->ItemIterator();
1520 	while (MenuItem* item = iterator.Next()) {
1521 		if (item->Type() == MENU_ITEM_SEPARATOR || !item->IsMarked()
1522 			|| item->Data() == NULL) {
1523 			continue;
1524 		}
1525 
1526 		char buffer[256];
1527 		if (snprintf(buffer, sizeof(buffer), "%s true\n",
1528 				(const char*)item->Data()) >= (int)sizeof(buffer)
1529 			|| !sSafeModeOptionsBuffer.Append(buffer)) {
1530 			dprintf("apply_safe_mode_options(): failed to append option to "
1531 				"buffer\n");
1532 		}
1533 	}
1534 }
1535 
1536 
1537 static void
1538 apply_safe_mode_path_blocklist()
1539 {
1540 	if (sPathBlocklist->IsEmpty())
1541 		return;
1542 
1543 	bool success = sSafeModeOptionsBuffer.Append("BlockedEntries {\n");
1544 
1545 	for (PathBlocklist::Iterator it = sPathBlocklist->GetIterator();
1546 		BlockedPath* path = it.Next();) {
1547 		success &= sSafeModeOptionsBuffer.Append(path->Path());
1548 		success &= sSafeModeOptionsBuffer.Append("\n", 1);
1549 	}
1550 
1551 	success &= sSafeModeOptionsBuffer.Append("}\n");
1552 
1553 	// Append the option a second time using the legacy name, so it also works
1554 	// for older kernel.
1555 	// TODO remove this after R1 beta3 is released and no one is using older
1556 	// kernels anymore.
1557 	success &= sSafeModeOptionsBuffer.Append("EntryBlacklist {\n");
1558 
1559 	for (PathBlocklist::Iterator it = sPathBlocklist->GetIterator();
1560 		BlockedPath* path = it.Next();) {
1561 		success &= sSafeModeOptionsBuffer.Append(path->Path());
1562 		success &= sSafeModeOptionsBuffer.Append("\n", 1);
1563 	}
1564 
1565 	success &= sSafeModeOptionsBuffer.Append("}\n");
1566 
1567 	if (!success) {
1568 		dprintf("apply_safe_mode_options(): failed to append path "
1569 			"blocklist to buffer\n");
1570 	}
1571 }
1572 
1573 
1574 static bool
1575 user_menu_reboot(Menu* menu, MenuItem* item)
1576 {
1577 	platform_exit();
1578 	return true;
1579 }
1580 
1581 
1582 status_t
1583 user_menu(BootVolume& _bootVolume, PathBlocklist& _pathBlocklist)
1584 {
1585 
1586 	Menu* menu = new(std::nothrow) Menu(MAIN_MENU);
1587 
1588 	sMainMenu = menu;
1589 	sBootVolume = &_bootVolume;
1590 	sPathBlocklist = &_pathBlocklist;
1591 
1592 	Menu* safeModeMenu = NULL;
1593 	Menu* debugMenu = NULL;
1594 	MenuItem* item;
1595 
1596 	TRACE(("user_menu: enter\n"));
1597 
1598 	// Add boot volume
1599 	menu->AddItem(item = new(std::nothrow) MenuItem("Select boot volume/state",
1600 		add_boot_volume_menu()));
1601 
1602 	// Add safe mode
1603 	menu->AddItem(item = new(std::nothrow) MenuItem("Select safe mode options",
1604 		safeModeMenu = add_safe_mode_menu()));
1605 
1606 	// add debug menu
1607 	menu->AddItem(item = new(std::nothrow) MenuItem("Select debug options",
1608 		debugMenu = add_debug_menu()));
1609 
1610 	// Add platform dependent menus
1611 	platform_add_menus(menu);
1612 
1613 	menu->AddSeparatorItem();
1614 
1615 	menu->AddItem(item = new(std::nothrow) MenuItem("Reboot"));
1616 	item->SetTarget(user_menu_reboot);
1617 	item->SetShortcut('r');
1618 
1619 	menu->AddItem(item = new(std::nothrow) MenuItem("Continue booting"));
1620 	if (!_bootVolume.IsValid()) {
1621 		item->SetLabel("Cannot continue booting (Boot volume is not valid)");
1622 		item->SetEnabled(false);
1623 		menu->ItemAt(0)->Select(true);
1624 	} else
1625 		item->SetShortcut('b');
1626 
1627 	menu->Run();
1628 
1629 	apply_safe_mode_options(safeModeMenu);
1630 	apply_safe_mode_options(debugMenu);
1631 	apply_safe_mode_path_blocklist();
1632 	add_safe_mode_settings(sSafeModeOptionsBuffer.String());
1633 	delete menu;
1634 
1635 
1636 	TRACE(("user_menu: leave\n"));
1637 
1638 	sMainMenu = NULL;
1639 	sBlocklistRootMenu = NULL;
1640 	sBootVolume = NULL;
1641 	sPathBlocklist = NULL;
1642 	return B_OK;
1643 }
1644