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