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