bootlst.c revision 6423:437422a29d3a
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25#pragma ident	"%Z%%M%	%I%	%E% SMI"
26
27#include <sys/promif.h>
28#include <sys/salib.h>
29
30#define	MAX_CMDLINE	1600  /* from GRUB source */
31
32char **titles;
33char **datasets;
34
35int	menu_entry_count;
36int	menu_table_size;
37
38int	in_menu_entry;
39
40#define	ENTRY_ALLOC_COUNT	10
41
42extern void	set_default_fs(char *fsw_name);
43extern int	mountroot(char *str);
44
45void
46init_table(void)
47{
48
49	menu_entry_count = 0;
50	titles = (char **)calloc(ENTRY_ALLOC_COUNT, sizeof (char *));
51	datasets = (char **)calloc(ENTRY_ALLOC_COUNT, sizeof (char *));
52	if (titles == NULL || datasets == NULL)
53		prom_panic("out of mem");
54	menu_table_size = ENTRY_ALLOC_COUNT;
55	in_menu_entry = 0;
56}
57
58void
59add_title_entry(char *title_str)
60{
61
62	/* skip leading white space */
63	while (isspace(*title_str))
64		title_str++;
65
66	if (menu_entry_count == menu_table_size) {
67		printf("Reallocating at count %d\n", menu_table_size);
68		titles = (char **)realloc(titles,
69		    ENTRY_ALLOC_COUNT * sizeof (char *));
70		datasets = (char **)realloc(datasets,
71		    ENTRY_ALLOC_COUNT * sizeof (char *));
72		if (titles == NULL || datasets == NULL)
73			prom_panic("out of mem");
74		menu_table_size += ENTRY_ALLOC_COUNT;
75	}
76
77	if (in_menu_entry)
78		free(titles[menu_entry_count]);
79	if ((titles[menu_entry_count] = strdup(title_str)) == NULL)
80		prom_panic("out of mem");
81	in_menu_entry = 1;
82}
83
84void
85add_dataset_entry(char *dataset_str)
86{
87	char	*cp;
88
89	/* skip leading white space */
90	while (isspace(*dataset_str))
91		dataset_str++;
92
93	/* if there is still any white space in the line, it's invalid */
94	for (cp = dataset_str; *cp; cp++)
95		if (isspace(*cp))
96			break;
97	if (*cp)
98		return;  /* dataset name was invalid */
99
100	if (!in_menu_entry)
101		return;	 /* dataset line was not preceded by a title */
102
103	if ((datasets[menu_entry_count] = strdup(dataset_str)) == NULL)
104		prom_panic("out of mem");
105	menu_entry_count++;
106	in_menu_entry = 0;
107}
108
109
110char *
111trim_white_space(char *cp)
112{
113	char	*ep;
114
115	/* skip leading white space */
116	while (isspace(*cp))
117		cp++;
118
119	/*
120	 *  if the string contained nothing but white space, return a
121	 *  null string.
122	 */
123	if (*cp == '\0')
124		return (cp);
125
126	/* truncate trailing white space */
127	for (ep = cp + strlen(cp) - 1; isspace(*ep); ep--)
128		;
129	ep++;
130	*ep = '\0';
131	return (cp);
132}
133
134char *cons_gets(char *, int);
135
136void
137main(void *cif)
138{
139	char linebuf[MAX_CMDLINE];
140	FILE	*file;
141	char	*cp, *ep;
142	int	n;
143	unsigned long	choice;
144
145	prom_init("bootlst", cif);
146	set_default_fs("promfs");
147	if (mountroot("bootfs") != 0)
148		prom_panic("can't mount root");
149
150	if ((file = fopen("/boot/menu.lst", "r")) == NULL)
151		prom_panic("can't open menu.lst");
152	init_table();
153
154	while (fgets(linebuf, MAX_CMDLINE, file)) {
155		cp = trim_white_space(linebuf);
156
157		/* skip comments and blank lines */
158		if (*cp == '#' || *cp == '\0')
159			continue;
160
161		/* find end of first keyword on line */
162		for (ep = cp; !isspace(*ep) && *ep; ep++)
163			;
164
165		/* if at the end of the line, the line had no arguments */
166		if (*ep == '\0')
167			continue;
168
169		*ep = '\0';
170
171		if (strcmp(cp, "title") == 0) {
172			add_title_entry(ep + 1);
173			continue;
174		}
175
176		if (strcmp(cp, "bootfs") == 0) {
177			add_dataset_entry(ep + 1);
178			continue;
179		}
180	}
181
182	if (menu_entry_count == 0)
183		prom_panic("no menu entries found");
184
185	for (n = 0; n < menu_entry_count; n++) {
186		printf("%d %s\n", n + 1, titles[n]);
187	}
188
189	printf("Select environment to boot: [ 1 - %d ]: ", menu_entry_count);
190
191	while (cons_gets(linebuf, MAX_CMDLINE)) {
192		/* cut off leading and trailing white space */
193		cp = trim_white_space(linebuf);
194		choice = strtoul(cp, NULL, 0);
195
196		/*
197		 * If the input is totally invalid, the return value of
198		 * strtoul() will be 0 or ULONG_MAX.  Either way, it's
199		 * of the acceptable range.
200		 */
201		if (choice == 0 || choice > menu_entry_count) {
202			printf("Invalid entry.\n");
203			continue;
204		}
205		/* XXX here is the result */
206		printf("\nTo boot the selected entry, invoke:\n");
207		printf("boot [<root-device>] -Z %s\n\n", datasets[choice - 1]);
208		prom_exit_to_mon();
209	}
210}
211