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 
32 char **titles;
33 char **datasets;
34 
35 int	menu_entry_count;
36 int	menu_table_size;
37 
38 int	in_menu_entry;
39 
40 #define	ENTRY_ALLOC_COUNT	10
41 
42 extern void	set_default_fs(char *fsw_name);
43 extern int	mountroot(char *str);
44 
45 void
init_table(void)46 init_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 
58 void
add_title_entry(char * title_str)59 add_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 
84 void
add_dataset_entry(char * dataset_str)85 add_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 
110 char *
trim_white_space(char * cp)111 trim_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 
134 char *cons_gets(char *, int);
135 
136 void
main(void * cif)137 main(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