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