1 /* 2 * Copyright 2003-2010, Axel Dörfler, axeld@pinc-software.de. 3 * Copyright 2011, Rene Gollent, rene@gollent.com. 4 * Distributed under the terms of the MIT License. 5 */ 6 7 8 #include "menu.h" 9 10 #include <errno.h> 11 #include <string.h> 12 13 #include <algorithm> 14 15 #include <OS.h> 16 17 #include <boot/menu.h> 18 #include <boot/stage2.h> 19 #include <boot/vfs.h> 20 #include <boot/platform.h> 21 #include <boot/platform/generic/text_console.h> 22 #include <boot/stdio.h> 23 #include <safemode.h> 24 #include <util/kernel_cpp.h> 25 #include <util/ring_buffer.h> 26 27 #include "kernel_debug_config.h" 28 29 #include "load_driver_settings.h" 30 #include "loader.h" 31 #include "pager.h" 32 #include "RootFileSystem.h" 33 34 35 #define TRACE_MENU 36 #ifdef TRACE_MENU 37 # define TRACE(x) dprintf x 38 #else 39 # define TRACE(x) ; 40 #endif 41 42 43 static char sSafeModeOptionsBuffer[2048]; 44 45 46 MenuItem::MenuItem(const char *label, Menu *subMenu) 47 : 48 fLabel(strdup(label)), 49 fTarget(NULL), 50 fIsMarked(false), 51 fIsSelected(false), 52 fIsEnabled(true), 53 fType(MENU_ITEM_STANDARD), 54 fMenu(NULL), 55 fSubMenu(subMenu), 56 fData(NULL), 57 fHelpText(NULL), 58 fShortcut(0) 59 { 60 if (subMenu != NULL) 61 subMenu->fSuperItem = this; 62 } 63 64 65 MenuItem::~MenuItem() 66 { 67 if (fSubMenu != NULL) 68 fSubMenu->fSuperItem = NULL; 69 70 free(const_cast<char *>(fLabel)); 71 } 72 73 74 void 75 MenuItem::SetTarget(menu_item_hook target) 76 { 77 fTarget = target; 78 } 79 80 81 /** Marks or unmarks a menu item. A marked menu item usually gets a visual 82 * clue like a checkmark that distinguishes it from others. 83 * For menus of type CHOICE_MENU, there can only be one marked item - the 84 * chosen one. 85 */ 86 87 void 88 MenuItem::SetMarked(bool marked) 89 { 90 if (marked && fMenu != NULL && fMenu->Type() == CHOICE_MENU) { 91 // always set choice text of parent if we were marked 92 fMenu->SetChoiceText(Label()); 93 } 94 95 if (fIsMarked == marked) 96 return; 97 98 if (marked && fMenu != NULL && fMenu->Type() == CHOICE_MENU) { 99 // unmark previous item 100 MenuItem *markedItem = fMenu->FindMarked(); 101 if (markedItem != NULL) 102 markedItem->SetMarked(false); 103 } 104 105 fIsMarked = marked; 106 107 if (fMenu != NULL) 108 fMenu->Draw(this); 109 } 110 111 112 void 113 MenuItem::Select(bool selected) 114 { 115 if (fIsSelected == selected) 116 return; 117 118 if (selected && fMenu != NULL) { 119 // unselect previous item 120 MenuItem *selectedItem = fMenu->FindSelected(); 121 if (selectedItem != NULL) 122 selectedItem->Select(false); 123 } 124 125 fIsSelected = selected; 126 127 if (fMenu != NULL) 128 fMenu->Draw(this); 129 } 130 131 132 void 133 MenuItem::SetType(menu_item_type type) 134 { 135 fType = type; 136 } 137 138 139 void 140 MenuItem::SetEnabled(bool enabled) 141 { 142 if (fIsEnabled == enabled) 143 return; 144 145 fIsEnabled = enabled; 146 147 if (fMenu != NULL) 148 fMenu->Draw(this); 149 } 150 151 152 void 153 MenuItem::SetData(const void *data) 154 { 155 fData = data; 156 } 157 158 159 /*! This sets a help text that is shown when the item is 160 selected. 161 Note, unlike the label, the string is not copied, it's 162 just referenced and has to stay valid as long as the 163 item's menu is being used. 164 */ 165 void 166 MenuItem::SetHelpText(const char* text) 167 { 168 fHelpText = text; 169 } 170 171 172 void 173 MenuItem::SetShortcut(char key) 174 { 175 fShortcut = key; 176 } 177 178 179 void 180 MenuItem::SetMenu(Menu* menu) 181 { 182 fMenu = menu; 183 } 184 185 186 // #pragma mark - 187 188 189 Menu::Menu(menu_type type, const char* title) 190 : 191 fTitle(title), 192 fChoiceText(NULL), 193 fCount(0), 194 fIsHidden(true), 195 fType(type), 196 fSuperItem(NULL), 197 fShortcuts(NULL) 198 { 199 } 200 201 202 Menu::~Menu() 203 { 204 // take all remaining items with us 205 206 MenuItem *item; 207 while ((item = fItems.Head()) != NULL) { 208 fItems.Remove(item); 209 delete item; 210 } 211 } 212 213 214 MenuItem* 215 Menu::ItemAt(int32 index) 216 { 217 if (index < 0 || index >= fCount) 218 return NULL; 219 220 MenuItemIterator iterator = ItemIterator(); 221 MenuItem *item; 222 223 while ((item = iterator.Next()) != NULL) { 224 if (index-- == 0) 225 return item; 226 } 227 228 return NULL; 229 } 230 231 232 int32 233 Menu::IndexOf(MenuItem* searchedItem) 234 { 235 int32 index = 0; 236 237 MenuItemIterator iterator = ItemIterator(); 238 while (MenuItem* item = iterator.Next()) { 239 if (item == searchedItem) 240 return index; 241 242 index++; 243 } 244 245 return -1; 246 } 247 248 249 int32 250 Menu::CountItems() const 251 { 252 return fCount; 253 } 254 255 256 MenuItem* 257 Menu::FindItem(const char* label) 258 { 259 MenuItemIterator iterator = ItemIterator(); 260 while (MenuItem* item = iterator.Next()) { 261 if (item->Label() != NULL && !strcmp(item->Label(), label)) 262 return item; 263 } 264 265 return NULL; 266 } 267 268 269 MenuItem* 270 Menu::FindMarked() 271 { 272 MenuItemIterator iterator = ItemIterator(); 273 while (MenuItem* item = iterator.Next()) { 274 if (item->IsMarked()) 275 return item; 276 } 277 278 return NULL; 279 } 280 281 282 MenuItem* 283 Menu::FindSelected(int32* _index) 284 { 285 int32 index = 0; 286 287 MenuItemIterator iterator = ItemIterator(); 288 while (MenuItem* item = iterator.Next()) { 289 if (item->IsSelected()) { 290 if (_index != NULL) 291 *_index = index; 292 return item; 293 } 294 295 index++; 296 } 297 298 return NULL; 299 } 300 301 302 void 303 Menu::AddItem(MenuItem* item) 304 { 305 item->fMenu = this; 306 fItems.Add(item); 307 fCount++; 308 } 309 310 311 status_t 312 Menu::AddSeparatorItem() 313 { 314 MenuItem* item = new(std::nothrow) MenuItem(); 315 if (item == NULL) 316 return B_NO_MEMORY; 317 318 item->SetType(MENU_ITEM_SEPARATOR); 319 320 AddItem(item); 321 return B_OK; 322 } 323 324 325 MenuItem* 326 Menu::RemoveItemAt(int32 index) 327 { 328 if (index < 0 || index >= fCount) 329 return NULL; 330 331 MenuItemIterator iterator = ItemIterator(); 332 while (MenuItem* item = iterator.Next()) { 333 if (index-- == 0) { 334 RemoveItem(item); 335 return item; 336 } 337 } 338 339 return NULL; 340 } 341 342 343 void 344 Menu::RemoveItem(MenuItem* item) 345 { 346 item->fMenu = NULL; 347 fItems.Remove(item); 348 fCount--; 349 } 350 351 352 void 353 Menu::AddShortcut(char key, shortcut_hook function) 354 { 355 Menu::shortcut* shortcut = new(std::nothrow) Menu::shortcut; 356 if (shortcut == NULL) 357 return; 358 359 shortcut->key = key; 360 shortcut->function = function; 361 362 shortcut->next = fShortcuts; 363 fShortcuts = shortcut; 364 } 365 366 367 shortcut_hook 368 Menu::FindShortcut(char key) const 369 { 370 if (key == 0) 371 return NULL; 372 373 const Menu::shortcut* shortcut = fShortcuts; 374 while (shortcut != NULL) { 375 if (shortcut->key == key) 376 return shortcut->function; 377 378 shortcut = shortcut->next; 379 } 380 381 return NULL; 382 } 383 384 385 MenuItem* 386 Menu::FindItemByShortcut(char key) 387 { 388 if (key == 0) 389 return NULL; 390 391 MenuItemList::Iterator iterator = ItemIterator(); 392 while (MenuItem* item = iterator.Next()) { 393 if (item->Shortcut() == key) 394 return item; 395 } 396 397 return NULL; 398 } 399 400 401 void 402 Menu::Run() 403 { 404 platform_run_menu(this); 405 } 406 407 408 void 409 Menu::Draw(MenuItem* item) 410 { 411 if (!IsHidden()) 412 platform_update_menu_item(this, item); 413 } 414 415 416 // #pragma mark - 417 418 419 static const char* 420 size_to_string(off_t size, char* buffer, size_t bufferSize) 421 { 422 static const char* const kPrefixes[] = { "K", "M", "G", "T", "P", NULL }; 423 int32 nextIndex = 0; 424 int32 remainder = 0; 425 while (size >= 1024 && kPrefixes[nextIndex] != NULL) { 426 remainder = size % 1024; 427 size /= 1024; 428 nextIndex++; 429 430 if (size < 1024) { 431 // Compute the decimal remainder and make sure we have at most 432 // 3 decimal places (or 4 for 1000 <= size <= 1023). 433 int32 factor; 434 if (size >= 100) 435 factor = 100; 436 else if (size >= 10) 437 factor = 10; 438 else 439 factor = 1; 440 441 remainder = (remainder * 1000 + 5 * factor) / 1024; 442 443 if (remainder >= 1000) { 444 size++; 445 remainder = 0; 446 } else 447 remainder /= 10 * factor; 448 } else 449 size += (remainder + 512) / 1024; 450 } 451 452 if (remainder == 0) { 453 snprintf(buffer, bufferSize, "%" B_PRIdOFF, size); 454 } else { 455 snprintf(buffer, bufferSize, "%" B_PRIdOFF ".%" B_PRId32, size, 456 remainder); 457 } 458 459 size_t length = strlen(buffer); 460 snprintf(buffer + length, bufferSize - length, " %sB", 461 nextIndex == 0 ? "" : kPrefixes[nextIndex - 1]); 462 463 return buffer; 464 } 465 466 467 // #pragma mark - 468 469 470 static bool 471 user_menu_boot_volume(Menu* menu, MenuItem* item) 472 { 473 Menu* super = menu->Supermenu(); 474 if (super == NULL) { 475 // huh? 476 return true; 477 } 478 479 MenuItem *bootItem = super->ItemAt(super->CountItems() - 1); 480 bootItem->SetEnabled(true); 481 bootItem->Select(true); 482 bootItem->SetData(item->Data()); 483 484 gBootVolume.SetBool(BOOT_VOLUME_USER_SELECTED, true); 485 return true; 486 } 487 488 489 static bool 490 debug_menu_display_current_log(Menu* menu, MenuItem* item) 491 { 492 // get the buffer 493 size_t bufferSize; 494 const char* buffer = platform_debug_get_log_buffer(&bufferSize); 495 if (buffer == NULL || bufferSize == 0) 496 return true; 497 498 struct TextSource : PagerTextSource { 499 TextSource(const char* buffer, size_t size) 500 : 501 fBuffer(buffer), 502 fSize(strnlen(buffer, size)) 503 { 504 } 505 506 virtual size_t BytesAvailable() const 507 { 508 return fSize; 509 } 510 511 virtual size_t Read(size_t offset, void* buffer, size_t size) const 512 { 513 if (offset >= fSize) 514 return 0; 515 516 if (size > fSize - offset) 517 size = fSize - offset; 518 519 memcpy(buffer, fBuffer + offset, size); 520 return size; 521 } 522 523 private: 524 const char* fBuffer; 525 size_t fSize; 526 }; 527 528 pager(TextSource(buffer, bufferSize)); 529 530 return true; 531 } 532 533 534 static bool 535 debug_menu_display_previous_syslog(Menu* menu, MenuItem* item) 536 { 537 ring_buffer* buffer = (ring_buffer*)gKernelArgs.debug_output.Pointer(); 538 if (buffer == NULL) 539 return true; 540 541 struct TextSource : PagerTextSource { 542 TextSource(ring_buffer* buffer) 543 : 544 fBuffer(buffer) 545 { 546 } 547 548 virtual size_t BytesAvailable() const 549 { 550 return ring_buffer_readable(fBuffer); 551 } 552 553 virtual size_t Read(size_t offset, void* buffer, size_t size) const 554 { 555 return ring_buffer_peek(fBuffer, offset, buffer, size); 556 } 557 558 private: 559 ring_buffer* fBuffer; 560 }; 561 562 pager(TextSource(buffer)); 563 564 return true; 565 } 566 567 568 static status_t 569 save_previous_syslog_to_volume(Directory* directory) 570 { 571 // find an unused name 572 char name[16]; 573 bool found = false; 574 for (int i = 0; i < 99; i++) { 575 snprintf(name, sizeof(name), "SYSLOG%02d.TXT", i); 576 Node* node = directory->Lookup(name, false); 577 if (node == NULL) { 578 found = true; 579 break; 580 } 581 582 node->Release(); 583 } 584 585 if (!found) { 586 printf("Failed to find an unused name for the syslog file!\n"); 587 return B_ERROR; 588 } 589 590 printf("Writing syslog to file \"%s\" ...\n", name); 591 592 int fd = open_from(directory, name, O_RDWR | O_CREAT | O_EXCL, 0644); 593 if (fd < 0) { 594 printf("Failed to create syslog file!\n"); 595 return fd; 596 } 597 598 ring_buffer* syslogBuffer 599 = (ring_buffer*)gKernelArgs.debug_output.Pointer(); 600 iovec vecs[2]; 601 int32 vecCount = ring_buffer_get_vecs(syslogBuffer, vecs); 602 if (vecCount > 0) { 603 size_t toWrite = ring_buffer_readable(syslogBuffer); 604 605 ssize_t written = writev(fd, vecs, vecCount); 606 if (written < 0 || (size_t)written != toWrite) { 607 printf("Failed to write to the syslog file \"%s\"!\n", name); 608 close(fd); 609 return errno; 610 } 611 } 612 613 close(fd); 614 615 printf("Successfully wrote syslog file.\n"); 616 617 return B_OK; 618 } 619 620 621 static bool 622 debug_menu_add_advanced_option(Menu* menu, MenuItem* item) 623 { 624 char buffer[256]; 625 626 size_t size = platform_get_user_input_text(menu, item, buffer, 627 sizeof(buffer) - 1); 628 629 if (size > 0) { 630 buffer[size] = '\n'; 631 size_t pos = strlen(sSafeModeOptionsBuffer); 632 if (pos + size + 1 < sizeof(sSafeModeOptionsBuffer)) 633 strlcat(sSafeModeOptionsBuffer, buffer, 634 sizeof(sSafeModeOptionsBuffer)); 635 } 636 637 return true; 638 } 639 640 641 static bool 642 debug_menu_toggle_debug_syslog(Menu* menu, MenuItem* item) 643 { 644 gKernelArgs.keep_debug_output_buffer = item->IsMarked(); 645 return true; 646 } 647 648 649 static bool 650 debug_menu_save_previous_syslog(Menu* menu, MenuItem* item) 651 { 652 Directory* volume = (Directory*)item->Data(); 653 654 console_clear_screen(); 655 656 save_previous_syslog_to_volume(volume); 657 658 printf("\nPress any key to continue\n"); 659 console_wait_for_key(); 660 661 return true; 662 } 663 664 665 static Menu* 666 add_boot_volume_menu(Directory* bootVolume) 667 { 668 Menu* menu = new(std::nothrow) Menu(CHOICE_MENU, "Select Boot Volume"); 669 MenuItem* item; 670 void* cookie; 671 int32 count = 0; 672 673 if (gRoot->Open(&cookie, O_RDONLY) == B_OK) { 674 Directory* volume; 675 while (gRoot->GetNextNode(cookie, (Node**)&volume) == B_OK) { 676 // only list bootable volumes 677 if (!is_bootable(volume)) 678 continue; 679 680 char name[B_FILE_NAME_LENGTH]; 681 if (volume->GetName(name, sizeof(name)) == B_OK) { 682 menu->AddItem(item = new(nothrow) MenuItem(name)); 683 item->SetTarget(user_menu_boot_volume); 684 item->SetData(volume); 685 686 if (volume == bootVolume) { 687 item->SetMarked(true); 688 item->Select(true); 689 } 690 691 count++; 692 } 693 } 694 gRoot->Close(cookie); 695 } 696 697 if (count == 0) { 698 // no boot volume found yet 699 menu->AddItem(item = new(nothrow) MenuItem("<No boot volume found>")); 700 item->SetType(MENU_ITEM_NO_CHOICE); 701 item->SetEnabled(false); 702 } 703 704 menu->AddSeparatorItem(); 705 706 menu->AddItem(item = new(nothrow) MenuItem("Rescan volumes")); 707 item->SetHelpText("Please insert a Haiku CD-ROM or attach a USB disk - " 708 "depending on your system, you can then boot from there."); 709 item->SetType(MENU_ITEM_NO_CHOICE); 710 if (count == 0) 711 item->Select(true); 712 713 menu->AddItem(item = new(nothrow) MenuItem("Return to main menu")); 714 item->SetType(MENU_ITEM_NO_CHOICE); 715 716 if (gBootVolume.GetBool(BOOT_VOLUME_BOOTED_FROM_IMAGE, false)) 717 menu->SetChoiceText("CD-ROM or hard drive"); 718 719 return menu; 720 } 721 722 723 static Menu* 724 add_safe_mode_menu() 725 { 726 Menu* safeMenu = new(nothrow) Menu(SAFE_MODE_MENU, "Safe Mode Options"); 727 MenuItem* item; 728 729 safeMenu->AddItem(item = new(nothrow) MenuItem("Safe mode")); 730 item->SetData(B_SAFEMODE_SAFE_MODE); 731 item->SetType(MENU_ITEM_MARKABLE); 732 item->SetHelpText("Puts the system into safe mode. This can be enabled " 733 "independently from the other options."); 734 735 safeMenu->AddItem(item = new(nothrow) MenuItem("Disable user add-ons")); 736 item->SetData(B_SAFEMODE_DISABLE_USER_ADD_ONS); 737 item->SetType(MENU_ITEM_MARKABLE); 738 item->SetHelpText("Prevents all user installed add-ons from being loaded. " 739 "Only the add-ons in the system directory will be used."); 740 741 safeMenu->AddItem(item = new(nothrow) MenuItem("Disable IDE DMA")); 742 item->SetData(B_SAFEMODE_DISABLE_IDE_DMA); 743 item->SetType(MENU_ITEM_MARKABLE); 744 item->SetHelpText("Disables IDE DMA, increasing IDE compatibility " 745 "at the expense of performance."); 746 747 #if B_HAIKU_PHYSICAL_BITS > 32 748 // check whether we have memory beyond 4 GB 749 bool hasMemoryBeyond4GB = false; 750 for (uint32 i = 0; i < gKernelArgs.num_physical_memory_ranges; i++) { 751 addr_range& range = gKernelArgs.physical_memory_range[i]; 752 if (range.start >= (uint64)1 << 32) { 753 hasMemoryBeyond4GB = true; 754 break; 755 } 756 } 757 758 // ... add the menu, if so 759 if (hasMemoryBeyond4GB) { 760 safeMenu->AddItem( 761 item = new(nothrow) MenuItem("Ignore memory beyond 4 GiB")); 762 item->SetData(B_SAFEMODE_4_GB_MEMORY_LIMIT); 763 item->SetType(MENU_ITEM_MARKABLE); 764 item->SetHelpText("Ignores all memory beyond the 4 GiB address limit, " 765 "overriding the setting in the kernel settings file."); 766 } 767 #endif 768 769 platform_add_menus(safeMenu); 770 771 safeMenu->AddSeparatorItem(); 772 safeMenu->AddItem(item = new(nothrow) MenuItem("Return to main menu")); 773 774 return safeMenu; 775 } 776 777 778 static Menu* 779 add_save_debug_syslog_menu() 780 { 781 Menu* menu = new(nothrow) Menu(STANDARD_MENU, "Save syslog to volume ..."); 782 MenuItem* item; 783 784 const char* const kHelpText = "Currently only FAT32 volumes are supported. " 785 "Newly plugged in removable devices are only recognized after " 786 "rebooting."; 787 788 int32 itemsAdded = 0; 789 790 void* cookie; 791 if (gRoot->Open(&cookie, O_RDONLY) == B_OK) { 792 Node* node; 793 while (gRoot->GetNextNode(cookie, &node) == B_OK) { 794 Directory* volume = static_cast<Directory*>(node); 795 Partition* partition; 796 if (gRoot->GetPartitionFor(volume, &partition) != B_OK) 797 continue; 798 799 // we support only FAT32 volumes ATM 800 if (partition->content_type == NULL 801 || strcmp(partition->content_type, kPartitionTypeFAT32) != 0) { 802 continue; 803 } 804 805 char name[B_FILE_NAME_LENGTH]; 806 if (volume->GetName(name, sizeof(name)) != B_OK) 807 strlcpy(name, "unnamed", sizeof(name)); 808 809 // append offset, size, and type to the name 810 size_t len = strlen(name); 811 char offsetBuffer[32]; 812 char sizeBuffer[32]; 813 snprintf(name + len, sizeof(name) - len, 814 " (%s, offset %s, size %s)", partition->content_type, 815 size_to_string(partition->offset, offsetBuffer, 816 sizeof(offsetBuffer)), 817 size_to_string(partition->size, sizeBuffer, 818 sizeof(sizeBuffer))); 819 820 item = new(nothrow) MenuItem(name); 821 item->SetData(volume); 822 item->SetTarget(&debug_menu_save_previous_syslog); 823 item->SetType(MENU_ITEM_NO_CHOICE); 824 item->SetHelpText(kHelpText); 825 menu->AddItem(item); 826 itemsAdded++; 827 } 828 829 gRoot->Close(cookie); 830 } 831 832 if (itemsAdded == 0) { 833 menu->AddItem(item 834 = new(nothrow) MenuItem("No supported volumes found")); 835 item->SetType(MENU_ITEM_NO_CHOICE); 836 item->SetHelpText(kHelpText); 837 item->SetEnabled(false); 838 } 839 840 menu->AddSeparatorItem(); 841 menu->AddItem(item = new(nothrow) MenuItem("Return to debug menu")); 842 item->SetHelpText(kHelpText); 843 844 return menu; 845 } 846 847 848 static Menu* 849 add_debug_menu() 850 { 851 Menu* menu = new(std::nothrow) Menu(STANDARD_MENU, "Debug Options"); 852 MenuItem* item; 853 854 #if DEBUG_SPINLOCK_LATENCIES 855 item = new(std::nothrow) MenuItem("Disable latency checks"); 856 if (item != NULL) { 857 item->SetType(MENU_ITEM_MARKABLE); 858 item->SetData(B_SAFEMODE_DISABLE_LATENCY_CHECK); 859 item->SetHelpText("Disables latency check panics."); 860 menu->AddItem(item); 861 } 862 #endif 863 864 menu->AddItem(item 865 = new(nothrow) MenuItem("Enable serial debug output")); 866 item->SetData("serial_debug_output"); 867 item->SetType(MENU_ITEM_MARKABLE); 868 item->SetHelpText("Turns on forwarding the syslog output to the serial " 869 "interface (default: 115200, 8N1)."); 870 871 menu->AddItem(item 872 = new(nothrow) MenuItem("Enable on screen debug output")); 873 item->SetData("debug_screen"); 874 item->SetType(MENU_ITEM_MARKABLE); 875 item->SetHelpText("Displays debug output on screen while the system " 876 "is booting, instead of the normal boot logo."); 877 878 menu->AddItem(item 879 = new(nothrow) MenuItem("Disable on screen paging")); 880 item->SetData("disable_onscreen_paging"); 881 item->SetType(MENU_ITEM_MARKABLE); 882 item->SetHelpText("Disables paging when on screen debug output is " 883 "enabled."); 884 885 menu->AddItem(item = new(nothrow) MenuItem("Enable debug syslog")); 886 item->SetType(MENU_ITEM_MARKABLE); 887 item->SetMarked(gKernelArgs.keep_debug_output_buffer); 888 item->SetTarget(&debug_menu_toggle_debug_syslog); 889 item->SetHelpText("Enables a special in-memory syslog buffer for this " 890 "session that the boot loader will be able to access after rebooting."); 891 892 bool currentLogItemVisible = platform_debug_get_log_buffer(NULL) != NULL; 893 if (currentLogItemVisible) { 894 menu->AddSeparatorItem(); 895 menu->AddItem(item 896 = new(nothrow) MenuItem("Display current boot loader log")); 897 item->SetTarget(&debug_menu_display_current_log); 898 item->SetType(MENU_ITEM_NO_CHOICE); 899 item->SetHelpText( 900 "Displays the debug info the boot loader has logged."); 901 } 902 903 ring_buffer* syslogBuffer 904 = (ring_buffer*)gKernelArgs.debug_output.Pointer(); 905 if (syslogBuffer != NULL && ring_buffer_readable(syslogBuffer) > 0) { 906 if (!currentLogItemVisible) 907 menu->AddSeparatorItem(); 908 909 menu->AddItem(item 910 = new(nothrow) MenuItem("Display syslog from previous session")); 911 item->SetTarget(&debug_menu_display_previous_syslog); 912 item->SetType(MENU_ITEM_NO_CHOICE); 913 item->SetHelpText( 914 "Displays the syslog from the previous Haiku session."); 915 916 menu->AddItem(item = new(nothrow) MenuItem( 917 "Save syslog from previous session", add_save_debug_syslog_menu())); 918 item->SetHelpText("Saves the syslog from the previous Haiku session to " 919 "disk. Currently only FAT32 volumes are supported."); 920 } 921 922 menu->AddSeparatorItem(); 923 menu->AddItem(item = new(nothrow) MenuItem( 924 "Add advanced debug option")); 925 item->SetType(MENU_ITEM_NO_CHOICE); 926 item->SetTarget(&debug_menu_add_advanced_option); 927 item->SetHelpText( 928 "Allows advanced debugging options to be entered directly."); 929 930 menu->AddSeparatorItem(); 931 menu->AddItem(item = new(nothrow) MenuItem("Return to main menu")); 932 933 return menu; 934 } 935 936 937 static void 938 apply_safe_mode_options(Menu* menu) 939 { 940 int32 pos = strlen(sSafeModeOptionsBuffer); 941 size_t bufferSize = sizeof(sSafeModeOptionsBuffer); 942 943 MenuItemIterator iterator = menu->ItemIterator(); 944 while (MenuItem* item = iterator.Next()) { 945 if (item->Type() == MENU_ITEM_SEPARATOR || !item->IsMarked() 946 || item->Data() == NULL || (uint32)pos >= bufferSize) 947 continue; 948 949 size_t totalBytes = snprintf(sSafeModeOptionsBuffer + pos, 950 bufferSize - pos, "%s true\n", (const char*)item->Data()); 951 pos += std::min(totalBytes, bufferSize - pos - 1); 952 } 953 } 954 955 956 static bool 957 user_menu_reboot(Menu* menu, MenuItem* item) 958 { 959 platform_exit(); 960 return true; 961 } 962 963 964 status_t 965 user_menu(Directory** _bootVolume) 966 { 967 Menu* menu = new(std::nothrow) Menu(MAIN_MENU); 968 Menu* safeModeMenu = NULL; 969 Menu* debugMenu = NULL; 970 MenuItem* item; 971 972 TRACE(("user_menu: enter\n")); 973 974 memset(sSafeModeOptionsBuffer, 0, sizeof(sSafeModeOptionsBuffer)); 975 976 // Add boot volume 977 menu->AddItem(item = new(std::nothrow) MenuItem("Select boot volume", 978 add_boot_volume_menu(*_bootVolume))); 979 980 // Add safe mode 981 menu->AddItem(item = new(std::nothrow) MenuItem("Select safe mode options", 982 safeModeMenu = add_safe_mode_menu())); 983 984 // add debug menu 985 menu->AddItem(item = new(std::nothrow) MenuItem("Select debug options", 986 debugMenu = add_debug_menu())); 987 988 // Add platform dependent menus 989 platform_add_menus(menu); 990 991 menu->AddSeparatorItem(); 992 993 menu->AddItem(item = new(std::nothrow) MenuItem("Reboot")); 994 item->SetTarget(user_menu_reboot); 995 item->SetShortcut('r'); 996 997 menu->AddItem(item = new(std::nothrow) MenuItem("Continue booting")); 998 if (*_bootVolume == NULL) { 999 item->SetEnabled(false); 1000 menu->ItemAt(0)->Select(true); 1001 } else 1002 item->SetShortcut('b'); 1003 1004 menu->Run(); 1005 1006 // See if a new boot device has been selected, and propagate that back 1007 if (item->Data() != NULL) 1008 *_bootVolume = (Directory*)item->Data(); 1009 1010 apply_safe_mode_options(safeModeMenu); 1011 apply_safe_mode_options(debugMenu); 1012 add_safe_mode_settings(sSafeModeOptionsBuffer); 1013 delete menu; 1014 1015 1016 TRACE(("user_menu: leave\n")); 1017 1018 return B_OK; 1019 } 1020 1021