xref: /haiku/src/system/boot/loader/menu.cpp (revision 508f54795f39c3e7552d87c95aae9dd8ec6f505b)
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