xref: /haiku/src/system/boot/loader/menu.cpp (revision 44d5b89d66418cfd245fb43405bf8610ba4795b2)
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 
MenuItem(const char * label,Menu * subMenu)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 
~MenuItem()72 MenuItem::~MenuItem()
73 {
74 	delete fSubMenu;
75 	free(const_cast<char *>(fLabel));
76 }
77 
78 
79 void
SetTarget(menu_item_hook target)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
SetMarked(bool marked)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
Select(bool selected)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
SetType(menu_item_type type)138 MenuItem::SetType(menu_item_type type)
139 {
140 	fType = type;
141 }
142 
143 
144 void
SetEnabled(bool enabled)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
SetData(const void * data)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
SetHelpText(const char * text)171 MenuItem::SetHelpText(const char* text)
172 {
173 	fHelpText = text;
174 }
175 
176 
177 void
SetShortcut(char key)178 MenuItem::SetShortcut(char key)
179 {
180 	fShortcut = key;
181 }
182 
183 
184 void
SetLabel(const char * label)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
SetSubmenu(Menu * subMenu)195 MenuItem::SetSubmenu(Menu* subMenu)
196 {
197 	fSubMenu = subMenu;
198 
199 	if (fSubMenu != NULL)
200 		fSubMenu->fSuperItem = this;
201 }
202 
203 
204 void
SetMenu(Menu * menu)205 MenuItem::SetMenu(Menu* menu)
206 {
207 	fMenu = menu;
208 }
209 
210 
211 //	#pragma mark -
212 
213 
Menu(menu_type type,const char * title)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 
~Menu()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
Entered()240 Menu::Entered()
241 {
242 }
243 
244 
245 void
Exited()246 Menu::Exited()
247 {
248 }
249 
250 
251 MenuItem*
ItemAt(int32 index)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
IndexOf(MenuItem * searchedItem)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
CountItems() const287 Menu::CountItems() const
288 {
289 	return fCount;
290 }
291 
292 
293 MenuItem*
FindItem(const char * label)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*
FindMarked()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*
FindSelected(int32 * _index)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
AddItem(MenuItem * item)340 Menu::AddItem(MenuItem* item)
341 {
342 	item->fMenu = this;
343 	fItems.Add(item);
344 	fCount++;
345 }
346 
347 
348 status_t
AddSeparatorItem()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*
RemoveItemAt(int32 index)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
RemoveItem(MenuItem * item)381 Menu::RemoveItem(MenuItem* item)
382 {
383 	item->fMenu = NULL;
384 	fItems.Remove(item);
385 	fCount--;
386 }
387 
388 
389 void
AddShortcut(char key,shortcut_hook function)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
FindShortcut(char key) const405 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*
FindItemByShortcut(char key)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
SortItems(bool (* less)(const MenuItem *,const MenuItem *))449 Menu::SortItems(bool (*less)(const MenuItem*, const MenuItem*))
450 {
451 	fItems.Sort(less);
452 }
453 
454 
455 void
Run()456 Menu::Run()
457 {
458 	platform_run_menu(this);
459 }
460 
461 
462 void
Draw(MenuItem * item)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*
size_to_string(off_t size,char * buffer,size_t bufferSize)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:
BlocklistMenuItem(char * label,Node * node,Menu * subMenu)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 
~BlocklistMenuItem()536 	~BlocklistMenuItem()
537 	{
538 		fNode->Release();
539 
540 		// make sure the submenu is destroyed
541 		SetSubmenu(fSubMenu);
542 	}
543 
IsDirectoryItem() const544 	bool IsDirectoryItem() const
545 	{
546 		return fNode->Type() == S_IFDIR;
547 	}
548 
GetPath(BlockedPath & _path) const549 	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 
UpdateBlocked()562 	void UpdateBlocked()
563 	{
564 		BlockedPath path;
565 		if (GetPath(path))
566 			_SetMarked(sPathBlocklist->Contains(path.Path()), false);
567 	}
568 
SetMarked(bool marked)569 	virtual void SetMarked(bool marked)
570 	{
571 		_SetMarked(marked, true);
572 	}
573 
Less(const MenuItem * a,const MenuItem * b)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:
_SetMarked(bool marked,bool updateBlocklist)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:
BlocklistMenu()620 	BlocklistMenu()
621 		:
622 		Menu(STANDARD_MENU, kDefaultMenuTitle),
623 		fDirectory(NULL)
624 	{
625 	}
626 
~BlocklistMenu()627 	~BlocklistMenu()
628 	{
629 		SetDirectory(NULL);
630 	}
631 
Entered()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 
Exited()661 	virtual void Exited()
662 	{
663 		_DeleteItems();
664 	}
665 
666 protected:
SetDirectory(Directory * directory)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:
_CreateItem(Node * node)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 
_DeleteItems()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:
BlocklistRootMenu()732 	BlocklistRootMenu()
733 		:
734 		BlocklistMenu()
735 	{
736 	}
737 
Entered()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 
Exited()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:
BootVolumeMenuItem(const char * volumeName)774 	BootVolumeMenuItem(const char* volumeName)
775 		:
776 		MenuItem(volumeName),
777 		fStateChoiceText(NULL)
778 	{
779 	}
780 
~BootVolumeMenuItem()781 	~BootVolumeMenuItem()
782 	{
783 		UpdateStateName(NULL);
784 	}
785 
UpdateStateName(PackageVolumeState * volumeState)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:
PackageVolumeStateMenuItem(const char * label,PackageVolumeInfo * volumeInfo,PackageVolumeState * volumeState)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 
~PackageVolumeStateMenuItem()819 	~PackageVolumeStateMenuItem()
820 	{
821 		fVolumeInfo->ReleaseReference();
822 	}
823 
VolumeInfo() const824 	PackageVolumeInfo* VolumeInfo() const
825 	{
826 		return fVolumeInfo;
827 	}
828 
VolumeState() const829 	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:
StringBuffer()845 	StringBuffer()
846 		:
847 		fBuffer(NULL),
848 		fLength(0),
849 		fCapacity(0)
850 	{
851 	}
852 
~StringBuffer()853 	~StringBuffer()
854 	{
855 		free(fBuffer);
856 	}
857 
String() const858 	const char* String() const
859 	{
860 		return fBuffer != NULL ? fBuffer : "";
861 	}
862 
Length() const863 	size_t Length() const
864 	{
865 		return fLength;
866 	}
867 
Append(const char * toAppend)868 	bool Append(const char* toAppend)
869 	{
870 		return Append(toAppend, strlen(toAppend));
871 	}
872 
Append(const char * toAppend,size_t length)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:
_Resize(size_t newLength)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*
get_continue_booting_menu_item()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
update_continue_booting_menu_item(status_t status)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
user_menu_boot_volume(Menu * menu,MenuItem * item)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
user_menu_boot_volume_state(Menu * menu,MenuItem * _item)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
debug_menu_display_current_log(Menu * menu,MenuItem * item)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
debug_menu_display_previous_syslog(Menu * menu,MenuItem * item)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
save_previous_syslog_to_volume(Directory * directory)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
debug_menu_add_advanced_option(Menu * menu,MenuItem * item)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
debug_menu_toggle_debug_syslog(Menu * menu,MenuItem * item)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
debug_menu_toggle_previous_debug_syslog(Menu * menu,MenuItem * item)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
debug_menu_save_previous_syslog(Menu * menu,MenuItem * item)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
add_boot_volume_item(Menu * menu,Directory * volume,const char * name)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 	// Display the size of this boot partition, if we can.
1194 	Partition* partition;
1195 	if (gRoot->GetPartitionFor(volume, &partition) == B_OK) {
1196 		float size = partition->Size() / (1024.0 * 1024.0);
1197 		const char* unit = "MiB";
1198 		if (size > 1024.0) {
1199 			size /= 1024.0;
1200 			unit = "GiB";
1201 		}
1202 
1203 		char* newName = (char*)alloca(128);
1204 		snprintf(newName, 128, "%s (%f %s)", name, size, unit);
1205 		name = newName;
1206 	}
1207 
1208 	BootVolumeMenuItem* item = new(nothrow) BootVolumeMenuItem(name);
1209 	menu->AddItem(item);
1210 
1211 	Menu* subMenu = NULL;
1212 
1213 	if (volumeInfo != NULL && volumeInfo->LoadOldStates() == B_OK) {
1214 		subMenu = new(std::nothrow) Menu(CHOICE_MENU, "Select package activation state");
1215 
1216 		for (PackageVolumeStateList::ConstIterator it
1217 				= volumeInfo->States().GetIterator();
1218 			PackageVolumeState* state = it.Next();) {
1219 			PackageVolumeStateMenuItem* stateItem
1220 				= new(nothrow) PackageVolumeStateMenuItem(state->DisplayName(),
1221 					volumeInfo, state);
1222 			subMenu->AddItem(stateItem);
1223 			stateItem->SetTarget(user_menu_boot_volume_state);
1224 			stateItem->SetData(volume);
1225 
1226 			if (state == selectedState) {
1227 				stateItem->SetMarked(true);
1228 				stateItem->Select(true);
1229 				if (volume == sBootVolume->RootDirectory()) {
1230 					item->UpdateStateName(stateItem->VolumeState());
1231 				}
1232 			}
1233 		}
1234 	}
1235 
1236 	if (subMenu != NULL && subMenu->CountItems() > 1) {
1237 		item->SetHelpText(
1238 			"Enter to choose a different state to boot");
1239 		item->SetSubmenu(subMenu);
1240 	} else {
1241 		delete subMenu;
1242 		item->SetTarget(user_menu_boot_volume);
1243 		item->SetData(volume);
1244 	}
1245 
1246 	if (volume == sBootVolume->RootDirectory()) {
1247 		item->SetMarked(true);
1248 		item->Select(true);
1249 	}
1250 }
1251 
1252 
1253 static Menu*
add_boot_volume_menu()1254 add_boot_volume_menu()
1255 {
1256 	Menu* menu = new(std::nothrow) Menu(CHOICE_MENU, "Select Boot Volume/State");
1257 	MenuItem* item;
1258 	void* cookie;
1259 	int32 count = 0;
1260 
1261 	if (gRoot->Open(&cookie, O_RDONLY) == B_OK) {
1262 		Directory* volume;
1263 		while (gRoot->GetNextNode(cookie, (Node**)&volume) == B_OK) {
1264 			// only list bootable volumes
1265 			if (volume != sBootVolume->RootDirectory() && !is_bootable(volume))
1266 				continue;
1267 
1268 			char name[B_FILE_NAME_LENGTH];
1269 			if (volume->GetName(name, sizeof(name)) == B_OK) {
1270 				add_boot_volume_item(menu, volume, name);
1271 
1272 				count++;
1273 			}
1274 		}
1275 		gRoot->Close(cookie);
1276 	}
1277 
1278 	if (count == 0) {
1279 		// no boot volume found yet
1280 		menu->AddItem(item = new(nothrow) MenuItem("<No boot volume found>"));
1281 		item->SetType(MENU_ITEM_NO_CHOICE);
1282 		item->SetEnabled(false);
1283 	}
1284 
1285 	menu->AddSeparatorItem();
1286 
1287 	menu->AddItem(item = new(nothrow) MenuItem("Rescan volumes"));
1288 	item->SetHelpText("Please insert a Haiku CD-ROM or attach a USB disk - "
1289 		"depending on your system, you can then boot from there.");
1290 	item->SetType(MENU_ITEM_NO_CHOICE);
1291 	if (count == 0)
1292 		item->Select(true);
1293 
1294 	menu->AddItem(item = new(nothrow) MenuItem("Return to main menu"));
1295 	item->SetType(MENU_ITEM_NO_CHOICE);
1296 
1297 	if (gBootVolume.GetBool(BOOT_VOLUME_BOOTED_FROM_IMAGE, false))
1298 		menu->SetChoiceText("CD-ROM or hard drive");
1299 
1300 	return menu;
1301 }
1302 
1303 
1304 static Menu*
add_safe_mode_menu()1305 add_safe_mode_menu()
1306 {
1307 	Menu* safeMenu = new(nothrow) Menu(SAFE_MODE_MENU, "Safe Mode Options");
1308 	MenuItem* item;
1309 
1310 	safeMenu->AddItem(item = new(nothrow) MenuItem("Safe mode"));
1311 	item->SetData(B_SAFEMODE_SAFE_MODE);
1312 	item->SetType(MENU_ITEM_MARKABLE);
1313 	item->SetHelpText("Puts the system into safe mode. This can be enabled "
1314 		"independently from the other options.");
1315 
1316 	safeMenu->AddItem(item = new(nothrow) MenuItem("Disable user add-ons"));
1317 	item->SetData(B_SAFEMODE_DISABLE_USER_ADD_ONS);
1318 	item->SetType(MENU_ITEM_MARKABLE);
1319 	item->SetHelpText("Prevents all user installed add-ons from being loaded. "
1320 		"Only the add-ons in the system directory will be used.");
1321 
1322 	safeMenu->AddItem(item = new(nothrow) MenuItem("Disable IDE DMA"));
1323 	item->SetData(B_SAFEMODE_DISABLE_IDE_DMA);
1324 	item->SetType(MENU_ITEM_MARKABLE);
1325 	item->SetHelpText("Disables IDE DMA, increasing IDE compatibility "
1326 		"at the expense of performance.");
1327 
1328 #if B_HAIKU_PHYSICAL_BITS > 32
1329 	// check whether we have memory beyond 4 GB
1330 	bool hasMemoryBeyond4GB = false;
1331 	for (uint32 i = 0; i < gKernelArgs.num_physical_memory_ranges; i++) {
1332 		addr_range& range = gKernelArgs.physical_memory_range[i];
1333 		if (range.start >= (uint64)1 << 32) {
1334 			hasMemoryBeyond4GB = true;
1335 			break;
1336 		}
1337 	}
1338 
1339 	bool needs64BitPaging = true;
1340 		// TODO: Determine whether 64 bit paging (i.e. PAE for x86) is needed
1341 		// for other reasons (NX support).
1342 
1343 	// ... add the menu item, if so
1344 	if (hasMemoryBeyond4GB || needs64BitPaging) {
1345 		safeMenu->AddItem(
1346 			item = new(nothrow) MenuItem("Ignore memory beyond 4 GiB"));
1347 		item->SetData(B_SAFEMODE_4_GB_MEMORY_LIMIT);
1348 		item->SetType(MENU_ITEM_MARKABLE);
1349 		item->SetHelpText("Ignores all memory beyond the 4 GiB address limit, "
1350 			"overriding the setting in the kernel settings file.");
1351 	}
1352 #endif
1353 
1354 	platform_add_menus(safeMenu);
1355 
1356 	safeMenu->AddSeparatorItem();
1357 	sBlocklistRootMenu = new(std::nothrow) BlocklistRootMenu;
1358 	safeMenu->AddItem(item = new(std::nothrow) MenuItem(
1359 		"Disable system components", sBlocklistRootMenu));
1360 	item->SetHelpText("Allows to select system files that shall be ignored. "
1361 		"Useful e.g. to disable drivers temporarily.");
1362 
1363 	safeMenu->AddSeparatorItem();
1364 	safeMenu->AddItem(item = new(nothrow) MenuItem("Return to main menu"));
1365 
1366 	return safeMenu;
1367 }
1368 
1369 
1370 static Menu*
add_save_debug_syslog_menu()1371 add_save_debug_syslog_menu()
1372 {
1373 	Menu* menu = new(nothrow) Menu(STANDARD_MENU, "Save syslog to volume ...");
1374 	MenuItem* item;
1375 
1376 	const char* const kHelpText = "Currently only FAT32 volumes are supported. "
1377 		"Newly plugged in removable devices are only recognized after "
1378 		"rebooting.";
1379 
1380 	int32 itemsAdded = 0;
1381 
1382 	void* cookie;
1383 	if (gRoot->Open(&cookie, O_RDONLY) == B_OK) {
1384 		Node* node;
1385 		while (gRoot->GetNextNode(cookie, &node) == B_OK) {
1386 			Directory* volume = static_cast<Directory*>(node);
1387 			Partition* partition;
1388 			if (gRoot->GetPartitionFor(volume, &partition) != B_OK)
1389 				continue;
1390 
1391 			// we support only FAT32 volumes ATM
1392 			if (partition->content_type == NULL
1393 				|| strcmp(partition->content_type, kPartitionTypeFAT32) != 0) {
1394 				continue;
1395 			}
1396 
1397 			char name[B_FILE_NAME_LENGTH];
1398 			if (volume->GetName(name, sizeof(name)) != B_OK)
1399 				strlcpy(name, "unnamed", sizeof(name));
1400 
1401 			// append offset, size, and type to the name
1402 			size_t len = strlen(name);
1403 			char offsetBuffer[32];
1404 			char sizeBuffer[32];
1405 			snprintf(name + len, sizeof(name) - len,
1406 				" (%s, offset %s, size %s)", partition->content_type,
1407 				size_to_string(partition->offset, offsetBuffer,
1408 					sizeof(offsetBuffer)),
1409 				size_to_string(partition->size, sizeBuffer,
1410 					sizeof(sizeBuffer)));
1411 
1412 			item = new(nothrow) MenuItem(name);
1413 			item->SetData(volume);
1414 			item->SetTarget(&debug_menu_save_previous_syslog);
1415 			item->SetType(MENU_ITEM_NO_CHOICE);
1416 			item->SetHelpText(kHelpText);
1417 			menu->AddItem(item);
1418 			itemsAdded++;
1419 		}
1420 
1421 		gRoot->Close(cookie);
1422 	}
1423 
1424 	if (itemsAdded == 0) {
1425 		menu->AddItem(item
1426 			= new(nothrow) MenuItem("No supported volumes found"));
1427 		item->SetType(MENU_ITEM_NO_CHOICE);
1428 		item->SetHelpText(kHelpText);
1429 		item->SetEnabled(false);
1430 	}
1431 
1432 	menu->AddSeparatorItem();
1433 	menu->AddItem(item = new(nothrow) MenuItem("Return to debug menu"));
1434 	item->SetHelpText(kHelpText);
1435 
1436 	return menu;
1437 }
1438 
1439 
1440 static Menu*
add_debug_menu()1441 add_debug_menu()
1442 {
1443 	Menu* menu = new(std::nothrow) Menu(STANDARD_MENU, "Debug Options");
1444 	MenuItem* item;
1445 
1446 	menu->AddItem(item
1447 		= new(nothrow) MenuItem("Enable serial debug output"));
1448 	item->SetData("serial_debug_output");
1449 	item->SetType(MENU_ITEM_MARKABLE);
1450 	item->SetHelpText("Turns on forwarding the syslog output to the serial "
1451 		"interface (default: 115200, 8N1).");
1452 
1453 	menu->AddItem(item
1454 		= new(nothrow) MenuItem("Enable on screen debug output"));
1455 	item->SetData("debug_screen");
1456 	item->SetType(MENU_ITEM_MARKABLE);
1457 	item->SetHelpText("Displays debug output on screen while the system "
1458 		"is booting, instead of the normal boot logo.");
1459 
1460 	menu->AddItem(item
1461 		= new(nothrow) MenuItem("Disable on screen paging"));
1462 	item->SetData("disable_onscreen_paging");
1463 	item->SetType(MENU_ITEM_MARKABLE);
1464 	item->SetHelpText("Disables paging when on screen debug output is "
1465 		"enabled.");
1466 
1467 	menu->AddItem(item = new(nothrow) MenuItem("Enable debug syslog"));
1468 	item->SetType(MENU_ITEM_MARKABLE);
1469 	item->SetMarked(gKernelArgs.keep_debug_output_buffer);
1470 	item->SetTarget(&debug_menu_toggle_debug_syslog);
1471 	item->SetHelpText("Enables a special in-memory syslog buffer for this "
1472 		"session that the boot loader will be able to access after rebooting.");
1473 
1474 	ring_buffer* syslogBuffer
1475 		= (ring_buffer*)gKernelArgs.debug_output.Pointer();
1476 	bool hasPreviousSyslog
1477 		= syslogBuffer != NULL && ring_buffer_readable(syslogBuffer) > 0;
1478 	if (hasPreviousSyslog) {
1479 		menu->AddItem(item = new(nothrow) MenuItem(
1480 			"Save syslog from previous session during boot"));
1481 		item->SetType(MENU_ITEM_MARKABLE);
1482 		item->SetMarked(gKernelArgs.previous_debug_size);
1483 		item->SetTarget(&debug_menu_toggle_previous_debug_syslog);
1484 		item->SetHelpText("Saves the syslog from the previous Haiku session to "
1485 			"/var/log/previous_syslog when booting.");
1486 	}
1487 
1488 	bool currentLogItemVisible = platform_debug_get_log_buffer(NULL) != NULL;
1489 	if (currentLogItemVisible) {
1490 		menu->AddSeparatorItem();
1491 		menu->AddItem(item
1492 			= new(nothrow) MenuItem("Display current boot loader log"));
1493 		item->SetTarget(&debug_menu_display_current_log);
1494 		item->SetType(MENU_ITEM_NO_CHOICE);
1495 		item->SetHelpText(
1496 			"Displays the debug info the boot loader has logged.");
1497 	}
1498 
1499 	if (hasPreviousSyslog) {
1500 		if (!currentLogItemVisible)
1501 			menu->AddSeparatorItem();
1502 
1503 		menu->AddItem(item
1504 			= new(nothrow) MenuItem("Display syslog from previous session"));
1505 		item->SetTarget(&debug_menu_display_previous_syslog);
1506 		item->SetType(MENU_ITEM_NO_CHOICE);
1507 		item->SetHelpText(
1508 			"Displays the syslog from the previous Haiku session.");
1509 
1510 		menu->AddItem(item = new(nothrow) MenuItem(
1511 			"Save syslog from previous session", add_save_debug_syslog_menu()));
1512 		item->SetHelpText("Saves the syslog from the previous Haiku session to "
1513 			"disk. Currently only FAT32 volumes are supported.");
1514 	}
1515 
1516 	menu->AddSeparatorItem();
1517 	menu->AddItem(item = new(nothrow) MenuItem(
1518 		"Add advanced debug option"));
1519 	item->SetType(MENU_ITEM_NO_CHOICE);
1520 	item->SetTarget(&debug_menu_add_advanced_option);
1521 	item->SetHelpText(
1522 		"Allows advanced debugging options to be entered directly.");
1523 
1524 	menu->AddSeparatorItem();
1525 	menu->AddItem(item = new(nothrow) MenuItem("Return to main menu"));
1526 
1527 	return menu;
1528 }
1529 
1530 
1531 static void
apply_safe_mode_options(Menu * menu)1532 apply_safe_mode_options(Menu* menu)
1533 {
1534 	MenuItemIterator iterator = menu->ItemIterator();
1535 	while (MenuItem* item = iterator.Next()) {
1536 		if (item->Type() == MENU_ITEM_SEPARATOR || !item->IsMarked()
1537 			|| item->Data() == NULL) {
1538 			continue;
1539 		}
1540 
1541 		char buffer[256];
1542 		if (snprintf(buffer, sizeof(buffer), "%s true\n",
1543 				(const char*)item->Data()) >= (int)sizeof(buffer)
1544 			|| !sSafeModeOptionsBuffer.Append(buffer)) {
1545 			dprintf("apply_safe_mode_options(): failed to append option to "
1546 				"buffer\n");
1547 		}
1548 	}
1549 }
1550 
1551 
1552 static void
apply_safe_mode_path_blocklist()1553 apply_safe_mode_path_blocklist()
1554 {
1555 	if (sPathBlocklist->IsEmpty())
1556 		return;
1557 
1558 	bool success = sSafeModeOptionsBuffer.Append("BlockedEntries {\n");
1559 
1560 	for (PathBlocklist::Iterator it = sPathBlocklist->GetIterator();
1561 		BlockedPath* path = it.Next();) {
1562 		success &= sSafeModeOptionsBuffer.Append(path->Path());
1563 		success &= sSafeModeOptionsBuffer.Append("\n", 1);
1564 	}
1565 
1566 	success &= sSafeModeOptionsBuffer.Append("}\n");
1567 
1568 	if (!success) {
1569 		dprintf("apply_safe_mode_options(): failed to append path "
1570 			"blocklist to buffer\n");
1571 	}
1572 }
1573 
1574 
1575 static bool
user_menu_reboot(Menu * menu,MenuItem * item)1576 user_menu_reboot(Menu* menu, MenuItem* item)
1577 {
1578 	platform_exit();
1579 	return true;
1580 }
1581 
1582 
1583 status_t
user_menu(BootVolume & _bootVolume,PathBlocklist & _pathBlocklist)1584 user_menu(BootVolume& _bootVolume, PathBlocklist& _pathBlocklist)
1585 {
1586 
1587 	Menu* menu = new(std::nothrow) Menu(MAIN_MENU);
1588 
1589 	sMainMenu = menu;
1590 	sBootVolume = &_bootVolume;
1591 	sPathBlocklist = &_pathBlocklist;
1592 
1593 	Menu* safeModeMenu = NULL;
1594 	Menu* debugMenu = NULL;
1595 	MenuItem* item;
1596 
1597 	TRACE(("user_menu: enter\n"));
1598 
1599 	// Add boot volume
1600 	menu->AddItem(item = new(std::nothrow) MenuItem("Select boot volume/state",
1601 		add_boot_volume_menu()));
1602 
1603 	// Add safe mode
1604 	menu->AddItem(item = new(std::nothrow) MenuItem("Select safe mode options",
1605 		safeModeMenu = add_safe_mode_menu()));
1606 
1607 	// add debug menu
1608 	menu->AddItem(item = new(std::nothrow) MenuItem("Select debug options",
1609 		debugMenu = add_debug_menu()));
1610 
1611 	// Add platform dependent menus
1612 	platform_add_menus(menu);
1613 
1614 	menu->AddSeparatorItem();
1615 
1616 	menu->AddItem(item = new(std::nothrow) MenuItem("Reboot"));
1617 	item->SetTarget(user_menu_reboot);
1618 	item->SetShortcut('r');
1619 
1620 	menu->AddItem(item = new(std::nothrow) MenuItem("Continue booting"));
1621 	if (!_bootVolume.IsValid()) {
1622 		item->SetLabel("Cannot continue booting (Boot volume is not valid)");
1623 		item->SetEnabled(false);
1624 		menu->ItemAt(0)->Select(true);
1625 	} else
1626 		item->SetShortcut('b');
1627 
1628 	menu->Run();
1629 
1630 	apply_safe_mode_options(safeModeMenu);
1631 	apply_safe_mode_options(debugMenu);
1632 	apply_safe_mode_path_blocklist();
1633 	add_safe_mode_settings(sSafeModeOptionsBuffer.String());
1634 	delete menu;
1635 
1636 
1637 	TRACE(("user_menu: leave\n"));
1638 
1639 	sMainMenu = NULL;
1640 	sBlocklistRootMenu = NULL;
1641 	sBootVolume = NULL;
1642 	sPathBlocklist = NULL;
1643 	return B_OK;
1644 }
1645