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