xref: /onnv-gate/usr/src/lib/libgrubmgmt/common/libgrub_menu.c (revision 9160:1517e6edbc6f)
1*9160SSherry.Moore@Sun.COM /*
2*9160SSherry.Moore@Sun.COM  * CDDL HEADER START
3*9160SSherry.Moore@Sun.COM  *
4*9160SSherry.Moore@Sun.COM  * The contents of this file are subject to the terms of the
5*9160SSherry.Moore@Sun.COM  * Common Development and Distribution License (the "License").
6*9160SSherry.Moore@Sun.COM  * You may not use this file except in compliance with the License.
7*9160SSherry.Moore@Sun.COM  *
8*9160SSherry.Moore@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*9160SSherry.Moore@Sun.COM  * or http://www.opensolaris.org/os/licensing.
10*9160SSherry.Moore@Sun.COM  * See the License for the specific language governing permissions
11*9160SSherry.Moore@Sun.COM  * and limitations under the License.
12*9160SSherry.Moore@Sun.COM  *
13*9160SSherry.Moore@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
14*9160SSherry.Moore@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*9160SSherry.Moore@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
16*9160SSherry.Moore@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
17*9160SSherry.Moore@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
18*9160SSherry.Moore@Sun.COM  *
19*9160SSherry.Moore@Sun.COM  * CDDL HEADER END
20*9160SSherry.Moore@Sun.COM  */
21*9160SSherry.Moore@Sun.COM /*
22*9160SSherry.Moore@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23*9160SSherry.Moore@Sun.COM  * Use is subject to license terms.
24*9160SSherry.Moore@Sun.COM  */
25*9160SSherry.Moore@Sun.COM 
26*9160SSherry.Moore@Sun.COM /*
27*9160SSherry.Moore@Sun.COM  * This file contains functions for manipulating the GRUB menu.
28*9160SSherry.Moore@Sun.COM  */
29*9160SSherry.Moore@Sun.COM #include <stdio.h>
30*9160SSherry.Moore@Sun.COM #include <errno.h>
31*9160SSherry.Moore@Sun.COM #include <stdlib.h>
32*9160SSherry.Moore@Sun.COM #include <string.h>
33*9160SSherry.Moore@Sun.COM #include <unistd.h>
34*9160SSherry.Moore@Sun.COM #include <sys/types.h>
35*9160SSherry.Moore@Sun.COM #include <sys/mount.h>
36*9160SSherry.Moore@Sun.COM #include <stdarg.h>
37*9160SSherry.Moore@Sun.COM #include <assert.h>
38*9160SSherry.Moore@Sun.COM #include <ctype.h>
39*9160SSherry.Moore@Sun.COM 
40*9160SSherry.Moore@Sun.COM #include "libgrub_impl.h"
41*9160SSherry.Moore@Sun.COM 
42*9160SSherry.Moore@Sun.COM static const grub_cmd_desc_t grub_cmd_descs[GRBM_CMD_NUM] = {
43*9160SSherry.Moore@Sun.COM #define	menu_cmd(cmd, num, flag, parsef)	{cmd, num, flag},
44*9160SSherry.Moore@Sun.COM #include "libgrub_cmd.def"
45*9160SSherry.Moore@Sun.COM };
46*9160SSherry.Moore@Sun.COM 
47*9160SSherry.Moore@Sun.COM static void
48*9160SSherry.Moore@Sun.COM append_line(grub_menu_t *mp, grub_line_t *lp)
49*9160SSherry.Moore@Sun.COM {
50*9160SSherry.Moore@Sun.COM 	if (mp->gm_start == NULL) {
51*9160SSherry.Moore@Sun.COM 		mp->gm_start = lp;
52*9160SSherry.Moore@Sun.COM 	} else {
53*9160SSherry.Moore@Sun.COM 		mp->gm_end->gl_next = lp;
54*9160SSherry.Moore@Sun.COM 		lp->gl_prev = mp->gm_end;
55*9160SSherry.Moore@Sun.COM 	}
56*9160SSherry.Moore@Sun.COM 	mp->gm_end = lp;
57*9160SSherry.Moore@Sun.COM 	lp->gl_line_num = ++mp->gm_line_num;
58*9160SSherry.Moore@Sun.COM 	lp->gl_entry_num = GRUB_ENTRY_DEFAULT;
59*9160SSherry.Moore@Sun.COM }
60*9160SSherry.Moore@Sun.COM 
61*9160SSherry.Moore@Sun.COM static void
62*9160SSherry.Moore@Sun.COM process_line(grub_menu_t *mp)
63*9160SSherry.Moore@Sun.COM {
64*9160SSherry.Moore@Sun.COM 	int	n;
65*9160SSherry.Moore@Sun.COM 	grub_line_t	*lp;
66*9160SSherry.Moore@Sun.COM 
67*9160SSherry.Moore@Sun.COM 	lp = mp->gm_end;
68*9160SSherry.Moore@Sun.COM 	n = sizeof (grub_cmd_descs) / sizeof (grub_cmd_descs[0]);
69*9160SSherry.Moore@Sun.COM 
70*9160SSherry.Moore@Sun.COM 	/* search through the table of known commands */
71*9160SSherry.Moore@Sun.COM 	while (n-- != 0 && strcmp(lp->gl_cmd, grub_cmd_descs[n].gcd_cmd) != 0)
72*9160SSherry.Moore@Sun.COM 		;
73*9160SSherry.Moore@Sun.COM 
74*9160SSherry.Moore@Sun.COM 	/* unknown command */
75*9160SSherry.Moore@Sun.COM 	if (n < 0)
76*9160SSherry.Moore@Sun.COM 		return;
77*9160SSherry.Moore@Sun.COM 
78*9160SSherry.Moore@Sun.COM 	/* we found command, fill lp fields */
79*9160SSherry.Moore@Sun.COM 	lp->gl_flags = grub_cmd_descs[n].gcd_flags;
80*9160SSherry.Moore@Sun.COM 	lp->gl_cmdtp = grub_cmd_descs[n].gcd_num;
81*9160SSherry.Moore@Sun.COM }
82*9160SSherry.Moore@Sun.COM 
83*9160SSherry.Moore@Sun.COM 
84*9160SSherry.Moore@Sun.COM static void
85*9160SSherry.Moore@Sun.COM check_entry(grub_entry_t *ent)
86*9160SSherry.Moore@Sun.COM {
87*9160SSherry.Moore@Sun.COM 	int i;
88*9160SSherry.Moore@Sun.COM 	uint_t emask;
89*9160SSherry.Moore@Sun.COM 	grub_line_t *lp;
90*9160SSherry.Moore@Sun.COM 	const grub_line_t * const lend = ent->ge_end->gl_next;
91*9160SSherry.Moore@Sun.COM 
92*9160SSherry.Moore@Sun.COM 	emask = 0;
93*9160SSherry.Moore@Sun.COM 	for (i = 0, lp = ent->ge_start; lend != lp; lp = lp->gl_next, ++i) {
94*9160SSherry.Moore@Sun.COM 		lp->gl_entry_num = ent->ge_entry_num;
95*9160SSherry.Moore@Sun.COM 		if (lp->gl_flags == GRUB_LINE_INVALID ||
96*9160SSherry.Moore@Sun.COM 		    lp->gl_flags == GRUB_LINE_GLOBAL) {
97*9160SSherry.Moore@Sun.COM 			emask |= 1 << i;
98*9160SSherry.Moore@Sun.COM 			lp->gl_flags = GRUB_LINE_INVALID;
99*9160SSherry.Moore@Sun.COM 		}
100*9160SSherry.Moore@Sun.COM 	}
101*9160SSherry.Moore@Sun.COM 
102*9160SSherry.Moore@Sun.COM 	if ((ent->ge_emask = emask) == 0)
103*9160SSherry.Moore@Sun.COM 		ent->ge_flags |= GRBM_VALID_FLAG;
104*9160SSherry.Moore@Sun.COM }
105*9160SSherry.Moore@Sun.COM 
106*9160SSherry.Moore@Sun.COM static int
107*9160SSherry.Moore@Sun.COM add_entry(grub_menu_t *mp, grub_line_t *start, grub_line_t *end)
108*9160SSherry.Moore@Sun.COM {
109*9160SSherry.Moore@Sun.COM 	grub_entry_t *ent;
110*9160SSherry.Moore@Sun.COM 
111*9160SSherry.Moore@Sun.COM 	if ((ent = calloc(1, sizeof (*ent))) == NULL)
112*9160SSherry.Moore@Sun.COM 		return (errno);
113*9160SSherry.Moore@Sun.COM 
114*9160SSherry.Moore@Sun.COM 	ent->ge_start = start;
115*9160SSherry.Moore@Sun.COM 	ent->ge_end = end;
116*9160SSherry.Moore@Sun.COM 
117*9160SSherry.Moore@Sun.COM 	if (mp->gm_ent_end == NULL) {
118*9160SSherry.Moore@Sun.COM 		mp->gm_ent_start = ent;
119*9160SSherry.Moore@Sun.COM 	} else {
120*9160SSherry.Moore@Sun.COM 		mp->gm_ent_end->ge_next = ent;
121*9160SSherry.Moore@Sun.COM 		ent->ge_prev = mp->gm_ent_end;
122*9160SSherry.Moore@Sun.COM 	}
123*9160SSherry.Moore@Sun.COM 	mp->gm_ent_end = ent;
124*9160SSherry.Moore@Sun.COM 	ent->ge_entry_num = mp->gm_entry_num++;
125*9160SSherry.Moore@Sun.COM 	ent->ge_menu = mp;
126*9160SSherry.Moore@Sun.COM 	return (0);
127*9160SSherry.Moore@Sun.COM }
128*9160SSherry.Moore@Sun.COM 
129*9160SSherry.Moore@Sun.COM static void
130*9160SSherry.Moore@Sun.COM default_entry(grub_menu_t *mp)
131*9160SSherry.Moore@Sun.COM {
132*9160SSherry.Moore@Sun.COM 	uint_t defent;
133*9160SSherry.Moore@Sun.COM 	grub_line_t *lp;
134*9160SSherry.Moore@Sun.COM 	grub_entry_t *ent;
135*9160SSherry.Moore@Sun.COM 
136*9160SSherry.Moore@Sun.COM 	defent = 0;
137*9160SSherry.Moore@Sun.COM 	lp = mp->gm_curdefault;
138*9160SSherry.Moore@Sun.COM 
139*9160SSherry.Moore@Sun.COM 	if (lp != NULL && lp->gl_flags == GRUB_LINE_GLOBAL &&
140*9160SSherry.Moore@Sun.COM 	    lp->gl_cmdtp == GRBM_DEFAULT_CMD) {
141*9160SSherry.Moore@Sun.COM 		defent  = strtoul(lp->gl_arg, NULL, 0);
142*9160SSherry.Moore@Sun.COM 		if (defent >= mp->gm_entry_num)
143*9160SSherry.Moore@Sun.COM 			defent = 0;
144*9160SSherry.Moore@Sun.COM 	}
145*9160SSherry.Moore@Sun.COM 
146*9160SSherry.Moore@Sun.COM 	for (ent = mp->gm_ent_start; ent != NULL && defent != ent->ge_entry_num;
147*9160SSherry.Moore@Sun.COM 	    ent = ent->ge_next)
148*9160SSherry.Moore@Sun.COM 		;
149*9160SSherry.Moore@Sun.COM 
150*9160SSherry.Moore@Sun.COM 	mp->gm_ent_default = ent;
151*9160SSherry.Moore@Sun.COM }
152*9160SSherry.Moore@Sun.COM 
153*9160SSherry.Moore@Sun.COM static void
154*9160SSherry.Moore@Sun.COM free_line(grub_line_t *lp)
155*9160SSherry.Moore@Sun.COM {
156*9160SSherry.Moore@Sun.COM 	if (lp == NULL)
157*9160SSherry.Moore@Sun.COM 		return;
158*9160SSherry.Moore@Sun.COM 
159*9160SSherry.Moore@Sun.COM 	free(lp->gl_cmd);
160*9160SSherry.Moore@Sun.COM 	free(lp->gl_sep);
161*9160SSherry.Moore@Sun.COM 	free(lp->gl_arg);
162*9160SSherry.Moore@Sun.COM 	free(lp->gl_line);
163*9160SSherry.Moore@Sun.COM 	free(lp);
164*9160SSherry.Moore@Sun.COM }
165*9160SSherry.Moore@Sun.COM 
166*9160SSherry.Moore@Sun.COM static void
167*9160SSherry.Moore@Sun.COM free_linelist(grub_line_t *line)
168*9160SSherry.Moore@Sun.COM {
169*9160SSherry.Moore@Sun.COM 	grub_line_t *lp;
170*9160SSherry.Moore@Sun.COM 
171*9160SSherry.Moore@Sun.COM 	if (line == NULL)
172*9160SSherry.Moore@Sun.COM 		return;
173*9160SSherry.Moore@Sun.COM 
174*9160SSherry.Moore@Sun.COM 	while (line) {
175*9160SSherry.Moore@Sun.COM 		lp = line;
176*9160SSherry.Moore@Sun.COM 		line = lp->gl_next;
177*9160SSherry.Moore@Sun.COM 		free_line(lp);
178*9160SSherry.Moore@Sun.COM 	}
179*9160SSherry.Moore@Sun.COM }
180*9160SSherry.Moore@Sun.COM 
181*9160SSherry.Moore@Sun.COM static void
182*9160SSherry.Moore@Sun.COM free_entries(grub_menu_t *mp)
183*9160SSherry.Moore@Sun.COM {
184*9160SSherry.Moore@Sun.COM 	grub_entry_t *ent, *tmp;
185*9160SSherry.Moore@Sun.COM 
186*9160SSherry.Moore@Sun.COM 	if (mp == NULL)
187*9160SSherry.Moore@Sun.COM 		return;
188*9160SSherry.Moore@Sun.COM 
189*9160SSherry.Moore@Sun.COM 	for (ent = mp->gm_ent_start; (tmp = ent) != NULL;
190*9160SSherry.Moore@Sun.COM 	    ent = tmp->ge_next, free(tmp))
191*9160SSherry.Moore@Sun.COM 		;
192*9160SSherry.Moore@Sun.COM 
193*9160SSherry.Moore@Sun.COM 	mp->gm_ent_start = NULL;
194*9160SSherry.Moore@Sun.COM 	mp->gm_ent_end = NULL;
195*9160SSherry.Moore@Sun.COM }
196*9160SSherry.Moore@Sun.COM 
197*9160SSherry.Moore@Sun.COM static int
198*9160SSherry.Moore@Sun.COM grub_menu_append_line(grub_menu_t *mp, const char *line)
199*9160SSherry.Moore@Sun.COM {
200*9160SSherry.Moore@Sun.COM 	int rc;
201*9160SSherry.Moore@Sun.COM 	size_t n;
202*9160SSherry.Moore@Sun.COM 	grub_line_t *lp;
203*9160SSherry.Moore@Sun.COM 
204*9160SSherry.Moore@Sun.COM 	if (line == NULL)
205*9160SSherry.Moore@Sun.COM 		return (EINVAL);
206*9160SSherry.Moore@Sun.COM 
207*9160SSherry.Moore@Sun.COM 	rc = 0;
208*9160SSherry.Moore@Sun.COM 	lp = NULL;
209*9160SSherry.Moore@Sun.COM 	if ((lp = calloc(1, sizeof (*lp))) == NULL ||
210*9160SSherry.Moore@Sun.COM 	    (lp->gl_line = strdup(line)) == NULL) {
211*9160SSherry.Moore@Sun.COM 		free(lp);
212*9160SSherry.Moore@Sun.COM 		return (errno);
213*9160SSherry.Moore@Sun.COM 	}
214*9160SSherry.Moore@Sun.COM 
215*9160SSherry.Moore@Sun.COM 	/* skip initial white space */
216*9160SSherry.Moore@Sun.COM 	line += strspn(line, " \t");
217*9160SSherry.Moore@Sun.COM 
218*9160SSherry.Moore@Sun.COM 	/* process comment line */
219*9160SSherry.Moore@Sun.COM 	if (line[0] == '#') {
220*9160SSherry.Moore@Sun.COM 		if ((lp->gl_cmd =
221*9160SSherry.Moore@Sun.COM 		    strdup(grub_cmd_descs[GRBM_COMMENT_CMD].gcd_cmd)) == NULL ||
222*9160SSherry.Moore@Sun.COM 		    (lp->gl_sep =
223*9160SSherry.Moore@Sun.COM 		    strdup(grub_cmd_descs[GRBM_EMPTY_CMD].gcd_cmd)) == NULL ||
224*9160SSherry.Moore@Sun.COM 		    (lp->gl_arg = strdup(line + 1)) == NULL)
225*9160SSherry.Moore@Sun.COM 			rc = errno;
226*9160SSherry.Moore@Sun.COM 	} else {
227*9160SSherry.Moore@Sun.COM 		/* get command */
228*9160SSherry.Moore@Sun.COM 		n = strcspn(line, " \t=");
229*9160SSherry.Moore@Sun.COM 		if ((lp->gl_cmd = malloc(n + 1)) == NULL)
230*9160SSherry.Moore@Sun.COM 			rc = errno;
231*9160SSherry.Moore@Sun.COM 		else
232*9160SSherry.Moore@Sun.COM 			(void) strlcpy(lp->gl_cmd, line, n + 1);
233*9160SSherry.Moore@Sun.COM 
234*9160SSherry.Moore@Sun.COM 		line += n;
235*9160SSherry.Moore@Sun.COM 
236*9160SSherry.Moore@Sun.COM 		/* get separator */
237*9160SSherry.Moore@Sun.COM 		n = strspn(line, " \t=");
238*9160SSherry.Moore@Sun.COM 		if ((lp->gl_sep = malloc(n + 1)) == NULL)
239*9160SSherry.Moore@Sun.COM 			rc = errno;
240*9160SSherry.Moore@Sun.COM 		else
241*9160SSherry.Moore@Sun.COM 			(void) strlcpy(lp->gl_sep, line, n + 1);
242*9160SSherry.Moore@Sun.COM 
243*9160SSherry.Moore@Sun.COM 		line += n;
244*9160SSherry.Moore@Sun.COM 
245*9160SSherry.Moore@Sun.COM 		/* get arguments */
246*9160SSherry.Moore@Sun.COM 		if ((lp->gl_arg = strdup(line)) == NULL)
247*9160SSherry.Moore@Sun.COM 			rc = errno;
248*9160SSherry.Moore@Sun.COM 	}
249*9160SSherry.Moore@Sun.COM 
250*9160SSherry.Moore@Sun.COM 	if (rc != 0) {
251*9160SSherry.Moore@Sun.COM 		free_line(lp);
252*9160SSherry.Moore@Sun.COM 		return (rc);
253*9160SSherry.Moore@Sun.COM 	}
254*9160SSherry.Moore@Sun.COM 
255*9160SSherry.Moore@Sun.COM 	append_line(mp, lp);
256*9160SSherry.Moore@Sun.COM 	process_line(mp);
257*9160SSherry.Moore@Sun.COM 	return (0);
258*9160SSherry.Moore@Sun.COM }
259*9160SSherry.Moore@Sun.COM 
260*9160SSherry.Moore@Sun.COM static int
261*9160SSherry.Moore@Sun.COM grub_menu_process(grub_menu_t *mp)
262*9160SSherry.Moore@Sun.COM {
263*9160SSherry.Moore@Sun.COM 	int ret;
264*9160SSherry.Moore@Sun.COM 	grub_entry_t *ent;
265*9160SSherry.Moore@Sun.COM 	grub_line_t *line, *start;
266*9160SSherry.Moore@Sun.COM 
267*9160SSherry.Moore@Sun.COM 	/* Free remaininig entries, if any */
268*9160SSherry.Moore@Sun.COM 	free_entries(mp);
269*9160SSherry.Moore@Sun.COM 
270*9160SSherry.Moore@Sun.COM 	/*
271*9160SSherry.Moore@Sun.COM 	 * Walk through lines, till first 'title' command is encountered.
272*9160SSherry.Moore@Sun.COM 	 * Initialize globals.
273*9160SSherry.Moore@Sun.COM 	 */
274*9160SSherry.Moore@Sun.COM 	for (line = mp->gm_start; line != NULL; line = line->gl_next) {
275*9160SSherry.Moore@Sun.COM 		if (line->gl_flags == GRUB_LINE_GLOBAL &&
276*9160SSherry.Moore@Sun.COM 		    line->gl_cmdtp == GRBM_DEFAULT_CMD)
277*9160SSherry.Moore@Sun.COM 			mp->gm_curdefault = line;
278*9160SSherry.Moore@Sun.COM 		else if (line->gl_cmdtp == GRBM_TITLE_CMD)
279*9160SSherry.Moore@Sun.COM 			break;
280*9160SSherry.Moore@Sun.COM 	}
281*9160SSherry.Moore@Sun.COM 
282*9160SSherry.Moore@Sun.COM 	/*
283*9160SSherry.Moore@Sun.COM 	 * Walk through remaining lines and recreate menu entries.
284*9160SSherry.Moore@Sun.COM 	 */
285*9160SSherry.Moore@Sun.COM 	for (start = NULL; line != NULL; line = line->gl_next) {
286*9160SSherry.Moore@Sun.COM 		if (line->gl_cmdtp == GRBM_TITLE_CMD) {
287*9160SSherry.Moore@Sun.COM 			/* is first entry */
288*9160SSherry.Moore@Sun.COM 			if (start != NULL &&
289*9160SSherry.Moore@Sun.COM 			    (ret = add_entry(mp, start, line->gl_prev)) != 0)
290*9160SSherry.Moore@Sun.COM 				return (ret);
291*9160SSherry.Moore@Sun.COM 			start = line;
292*9160SSherry.Moore@Sun.COM 		}
293*9160SSherry.Moore@Sun.COM 	}
294*9160SSherry.Moore@Sun.COM 
295*9160SSherry.Moore@Sun.COM 	/* Add last entry */
296*9160SSherry.Moore@Sun.COM 	if (start != NULL && (ret = add_entry(mp, start, mp->gm_end)) != 0)
297*9160SSherry.Moore@Sun.COM 		return (ret);
298*9160SSherry.Moore@Sun.COM 
299*9160SSherry.Moore@Sun.COM 	for (ent = mp->gm_ent_start; NULL != ent; ent = ent->ge_next)
300*9160SSherry.Moore@Sun.COM 		check_entry(ent);
301*9160SSherry.Moore@Sun.COM 
302*9160SSherry.Moore@Sun.COM 	default_entry(mp);
303*9160SSherry.Moore@Sun.COM 
304*9160SSherry.Moore@Sun.COM 	return (0);
305*9160SSherry.Moore@Sun.COM }
306*9160SSherry.Moore@Sun.COM 
307*9160SSherry.Moore@Sun.COM static int
308*9160SSherry.Moore@Sun.COM grub_fs_init(grub_fs_t *fs)
309*9160SSherry.Moore@Sun.COM {
310*9160SSherry.Moore@Sun.COM 	assert(fs);
311*9160SSherry.Moore@Sun.COM 	if ((fs->gf_lzfh = libzfs_init()) == NULL ||
312*9160SSherry.Moore@Sun.COM 	    (fs->gf_diroot = di_init("/", DINFOCPYALL | DINFOPATH))
313*9160SSherry.Moore@Sun.COM 	    == DI_NODE_NIL ||
314*9160SSherry.Moore@Sun.COM 	    (fs->gf_dvlh = di_devlink_init(NULL, 0)) == DI_LINK_NIL) {
315*9160SSherry.Moore@Sun.COM 		return (EG_INITFS);
316*9160SSherry.Moore@Sun.COM 	}
317*9160SSherry.Moore@Sun.COM 	return (0);
318*9160SSherry.Moore@Sun.COM }
319*9160SSherry.Moore@Sun.COM 
320*9160SSherry.Moore@Sun.COM static void
321*9160SSherry.Moore@Sun.COM grub_fs_fini(grub_fs_t *fs)
322*9160SSherry.Moore@Sun.COM {
323*9160SSherry.Moore@Sun.COM 	if (fs == NULL)
324*9160SSherry.Moore@Sun.COM 		return;
325*9160SSherry.Moore@Sun.COM 
326*9160SSherry.Moore@Sun.COM 	if (fs->gf_dvlh != DI_LINK_NIL)
327*9160SSherry.Moore@Sun.COM 		(void) di_devlink_fini(&fs->gf_dvlh);
328*9160SSherry.Moore@Sun.COM 	if (fs->gf_diroot != DI_NODE_NIL)
329*9160SSherry.Moore@Sun.COM 		di_fini(fs->gf_diroot);
330*9160SSherry.Moore@Sun.COM 	if (fs->gf_lzfh != NULL)
331*9160SSherry.Moore@Sun.COM 		libzfs_fini(fs->gf_lzfh);
332*9160SSherry.Moore@Sun.COM 	(void) memset(fs, 0, sizeof (*fs));
333*9160SSherry.Moore@Sun.COM }
334*9160SSherry.Moore@Sun.COM 
335*9160SSherry.Moore@Sun.COM /*
336*9160SSherry.Moore@Sun.COM  * Reads and parses GRUB menu file into a grub_menu_t data structure.
337*9160SSherry.Moore@Sun.COM  * If grub_menu_path file path is NULL, will use 'currently active'
338*9160SSherry.Moore@Sun.COM  * GRUB menu file.
339*9160SSherry.Moore@Sun.COM  *
340*9160SSherry.Moore@Sun.COM  * Memory for the menu data structure is allocated within the routine.
341*9160SSherry.Moore@Sun.COM  * Caller must call grub_menu_fini() to release memory after calling
342*9160SSherry.Moore@Sun.COM  * grub_menu_init().
343*9160SSherry.Moore@Sun.COM  */
344*9160SSherry.Moore@Sun.COM int
345*9160SSherry.Moore@Sun.COM grub_menu_init(const char *path, grub_menu_t **menup)
346*9160SSherry.Moore@Sun.COM {
347*9160SSherry.Moore@Sun.COM 	FILE *fp;
348*9160SSherry.Moore@Sun.COM 	char *cp;
349*9160SSherry.Moore@Sun.COM 	grub_menu_t *mp;
350*9160SSherry.Moore@Sun.COM 	int len, n, ret;
351*9160SSherry.Moore@Sun.COM 	char buf[GRBM_MAXLINE];
352*9160SSherry.Moore@Sun.COM 
353*9160SSherry.Moore@Sun.COM 	if (menup == NULL)
354*9160SSherry.Moore@Sun.COM 		return (EINVAL);
355*9160SSherry.Moore@Sun.COM 
356*9160SSherry.Moore@Sun.COM 	/*
357*9160SSherry.Moore@Sun.COM 	 * Allocate space, perform initialization
358*9160SSherry.Moore@Sun.COM 	 */
359*9160SSherry.Moore@Sun.COM 	if ((mp = calloc(1, sizeof (*mp))) == NULL) {
360*9160SSherry.Moore@Sun.COM 		*menup = mp;
361*9160SSherry.Moore@Sun.COM 		return (errno);
362*9160SSherry.Moore@Sun.COM 	}
363*9160SSherry.Moore@Sun.COM 
364*9160SSherry.Moore@Sun.COM 	if ((ret = grub_fs_init(&mp->gm_fs)) != 0 ||
365*9160SSherry.Moore@Sun.COM 	    (ret = grub_current_root(&mp->gm_fs, &mp->gm_root)) != 0)
366*9160SSherry.Moore@Sun.COM 		goto err_out1;
367*9160SSherry.Moore@Sun.COM 
368*9160SSherry.Moore@Sun.COM 	if (path == NULL) {
369*9160SSherry.Moore@Sun.COM 		/*
370*9160SSherry.Moore@Sun.COM 		 * Use default grub-menu.
371*9160SSherry.Moore@Sun.COM 		 * If top dataset is not mounted, mount it now.
372*9160SSherry.Moore@Sun.COM 		 */
373*9160SSherry.Moore@Sun.COM 		if (mp->gm_root.gr_fs[GRBM_FS_TOP].gfs_mountp[0] == 0) {
374*9160SSherry.Moore@Sun.COM 			if ((ret = grub_fsd_mount_tmp(mp->gm_root.gr_fs +
375*9160SSherry.Moore@Sun.COM 			    GRBM_FS_TOP, mp->gm_root.gr_fstyp)) != 0)
376*9160SSherry.Moore@Sun.COM 				goto err_out1;
377*9160SSherry.Moore@Sun.COM 		}
378*9160SSherry.Moore@Sun.COM 		(void) snprintf(mp->gm_path, sizeof (mp->gm_path),
379*9160SSherry.Moore@Sun.COM 		    "%s/%s", mp->gm_root.gr_fs[GRBM_FS_TOP].gfs_mountp,
380*9160SSherry.Moore@Sun.COM 		    GRUB_MENU);
381*9160SSherry.Moore@Sun.COM 	} else {
382*9160SSherry.Moore@Sun.COM 		(void) strlcpy(mp->gm_path, path, sizeof (mp->gm_path));
383*9160SSherry.Moore@Sun.COM 	}
384*9160SSherry.Moore@Sun.COM 
385*9160SSherry.Moore@Sun.COM 	if ((fp = fopen(mp->gm_path, "r")) == NULL) {
386*9160SSherry.Moore@Sun.COM 		ret = errno;
387*9160SSherry.Moore@Sun.COM 		goto err_out1;
388*9160SSherry.Moore@Sun.COM 	}
389*9160SSherry.Moore@Sun.COM 
390*9160SSherry.Moore@Sun.COM 	cp = buf;
391*9160SSherry.Moore@Sun.COM 	len = sizeof (buf);
392*9160SSherry.Moore@Sun.COM 
393*9160SSherry.Moore@Sun.COM 	while (fgets(cp, len, fp) != NULL) {
394*9160SSherry.Moore@Sun.COM 
395*9160SSherry.Moore@Sun.COM 		if (IS_LINE2BIG(cp, len, n)) {
396*9160SSherry.Moore@Sun.COM 			ret = E2BIG;
397*9160SSherry.Moore@Sun.COM 			break;
398*9160SSherry.Moore@Sun.COM 		}
399*9160SSherry.Moore@Sun.COM 
400*9160SSherry.Moore@Sun.COM 		/* remove white space at the end of line */
401*9160SSherry.Moore@Sun.COM 		for (; isspace(cp[n - 1]); --n)
402*9160SSherry.Moore@Sun.COM 			;
403*9160SSherry.Moore@Sun.COM 		cp[n] = '\0';
404*9160SSherry.Moore@Sun.COM 
405*9160SSherry.Moore@Sun.COM 		if (cp[n - 1] == '\\') {
406*9160SSherry.Moore@Sun.COM 			len -= n - 1;
407*9160SSherry.Moore@Sun.COM 			assert(len >= 2);
408*9160SSherry.Moore@Sun.COM 			cp += n - 1;
409*9160SSherry.Moore@Sun.COM 			continue;
410*9160SSherry.Moore@Sun.COM 		}
411*9160SSherry.Moore@Sun.COM 		if ((ret = grub_menu_append_line(mp, buf)) != 0)
412*9160SSherry.Moore@Sun.COM 			break;
413*9160SSherry.Moore@Sun.COM 
414*9160SSherry.Moore@Sun.COM 		cp = buf;
415*9160SSherry.Moore@Sun.COM 		len = sizeof (buf);
416*9160SSherry.Moore@Sun.COM 	}
417*9160SSherry.Moore@Sun.COM 
418*9160SSherry.Moore@Sun.COM 	if (fclose(fp) == EOF)
419*9160SSherry.Moore@Sun.COM 		ret = errno;
420*9160SSherry.Moore@Sun.COM 	else if (ret == 0)
421*9160SSherry.Moore@Sun.COM 		ret = grub_menu_process(mp);
422*9160SSherry.Moore@Sun.COM 
423*9160SSherry.Moore@Sun.COM err_out1:
424*9160SSherry.Moore@Sun.COM 	grub_fsd_umount_tmp(mp->gm_root.gr_fs + GRBM_FS_TOP);
425*9160SSherry.Moore@Sun.COM 	if (0 != ret) {
426*9160SSherry.Moore@Sun.COM 		grub_menu_fini(mp);
427*9160SSherry.Moore@Sun.COM 		mp = NULL;
428*9160SSherry.Moore@Sun.COM 	}
429*9160SSherry.Moore@Sun.COM 	*menup = mp;
430*9160SSherry.Moore@Sun.COM 	return (ret);
431*9160SSherry.Moore@Sun.COM }
432*9160SSherry.Moore@Sun.COM 
433*9160SSherry.Moore@Sun.COM void
434*9160SSherry.Moore@Sun.COM grub_menu_fini(grub_menu_t *mp)
435*9160SSherry.Moore@Sun.COM {
436*9160SSherry.Moore@Sun.COM 	if (mp == NULL)
437*9160SSherry.Moore@Sun.COM 		return;
438*9160SSherry.Moore@Sun.COM 
439*9160SSherry.Moore@Sun.COM 	grub_fs_fini(&mp->gm_fs);
440*9160SSherry.Moore@Sun.COM 	free_entries(mp);
441*9160SSherry.Moore@Sun.COM 	free_linelist(mp->gm_start);
442*9160SSherry.Moore@Sun.COM 	free(mp);
443*9160SSherry.Moore@Sun.COM }
444*9160SSherry.Moore@Sun.COM 
445*9160SSherry.Moore@Sun.COM grub_line_t *
446*9160SSherry.Moore@Sun.COM grub_menu_next_line(const grub_menu_t *mp, const grub_line_t *lp)
447*9160SSherry.Moore@Sun.COM {
448*9160SSherry.Moore@Sun.COM 	assert(mp);
449*9160SSherry.Moore@Sun.COM 	if (lp == NULL)
450*9160SSherry.Moore@Sun.COM 		return (mp->gm_start);
451*9160SSherry.Moore@Sun.COM 	else
452*9160SSherry.Moore@Sun.COM 		return (lp->gl_next);
453*9160SSherry.Moore@Sun.COM }
454*9160SSherry.Moore@Sun.COM 
455*9160SSherry.Moore@Sun.COM grub_line_t *
456*9160SSherry.Moore@Sun.COM grub_menu_prev_line(const grub_menu_t *mp, const grub_line_t *lp)
457*9160SSherry.Moore@Sun.COM {
458*9160SSherry.Moore@Sun.COM 	assert(mp);
459*9160SSherry.Moore@Sun.COM 	if (lp == NULL)
460*9160SSherry.Moore@Sun.COM 		return (mp->gm_end);
461*9160SSherry.Moore@Sun.COM 	else
462*9160SSherry.Moore@Sun.COM 		return (lp->gl_prev);
463*9160SSherry.Moore@Sun.COM }
464*9160SSherry.Moore@Sun.COM 
465*9160SSherry.Moore@Sun.COM grub_line_t *
466*9160SSherry.Moore@Sun.COM grub_menu_get_line(const grub_menu_t *mp, int num)
467*9160SSherry.Moore@Sun.COM {
468*9160SSherry.Moore@Sun.COM 	grub_line_t *lp;
469*9160SSherry.Moore@Sun.COM 
470*9160SSherry.Moore@Sun.COM 	assert(mp);
471*9160SSherry.Moore@Sun.COM 	if (num > mp->gm_line_num)
472*9160SSherry.Moore@Sun.COM 		return (NULL);
473*9160SSherry.Moore@Sun.COM 	for (lp = mp->gm_start; lp != NULL && num != lp->gl_line_num;
474*9160SSherry.Moore@Sun.COM 	    lp = lp->gl_next)
475*9160SSherry.Moore@Sun.COM 		;
476*9160SSherry.Moore@Sun.COM 	return (lp);
477*9160SSherry.Moore@Sun.COM }
478*9160SSherry.Moore@Sun.COM 
479*9160SSherry.Moore@Sun.COM size_t
480*9160SSherry.Moore@Sun.COM grub_menu_get_cmdline(const grub_menu_t *mp, int num, char *cmdl, size_t size)
481*9160SSherry.Moore@Sun.COM {
482*9160SSherry.Moore@Sun.COM 	grub_entry_t *ent;
483*9160SSherry.Moore@Sun.COM 
484*9160SSherry.Moore@Sun.COM 	assert(mp);
485*9160SSherry.Moore@Sun.COM 	if ((ent = grub_menu_get_entry(mp, num)) == NULL)
486*9160SSherry.Moore@Sun.COM 		return (size_t)(-1);
487*9160SSherry.Moore@Sun.COM 
488*9160SSherry.Moore@Sun.COM 	return (grub_entry_get_cmdline(ent, cmdl, size));
489*9160SSherry.Moore@Sun.COM }
490*9160SSherry.Moore@Sun.COM 
491*9160SSherry.Moore@Sun.COM grub_entry_t *
492*9160SSherry.Moore@Sun.COM grub_menu_next_entry(const grub_menu_t *mp, const grub_entry_t *ent)
493*9160SSherry.Moore@Sun.COM {
494*9160SSherry.Moore@Sun.COM 	assert(mp);
495*9160SSherry.Moore@Sun.COM 	if (ent == NULL) {
496*9160SSherry.Moore@Sun.COM 		return (mp->gm_ent_start);
497*9160SSherry.Moore@Sun.COM 	} else {
498*9160SSherry.Moore@Sun.COM 		assert(mp == ent->ge_menu);
499*9160SSherry.Moore@Sun.COM 		return (ent->ge_next);
500*9160SSherry.Moore@Sun.COM 	}
501*9160SSherry.Moore@Sun.COM }
502*9160SSherry.Moore@Sun.COM 
503*9160SSherry.Moore@Sun.COM grub_entry_t *
504*9160SSherry.Moore@Sun.COM grub_menu_prev_entry(const grub_menu_t *mp, const grub_entry_t *ent)
505*9160SSherry.Moore@Sun.COM {
506*9160SSherry.Moore@Sun.COM 	assert(mp);
507*9160SSherry.Moore@Sun.COM 	if (ent == NULL) {
508*9160SSherry.Moore@Sun.COM 		return (mp->gm_ent_end);
509*9160SSherry.Moore@Sun.COM 	} else {
510*9160SSherry.Moore@Sun.COM 		assert(mp == ent->ge_menu);
511*9160SSherry.Moore@Sun.COM 		return (ent->ge_prev);
512*9160SSherry.Moore@Sun.COM 	}
513*9160SSherry.Moore@Sun.COM }
514*9160SSherry.Moore@Sun.COM 
515*9160SSherry.Moore@Sun.COM grub_entry_t *
516*9160SSherry.Moore@Sun.COM grub_menu_get_entry(const grub_menu_t *mp, int num)
517*9160SSherry.Moore@Sun.COM {
518*9160SSherry.Moore@Sun.COM 	grub_entry_t *ent;
519*9160SSherry.Moore@Sun.COM 
520*9160SSherry.Moore@Sun.COM 	assert(mp);
521*9160SSherry.Moore@Sun.COM 	if (num == GRUB_ENTRY_DEFAULT) {
522*9160SSherry.Moore@Sun.COM 		ent = mp->gm_ent_default;
523*9160SSherry.Moore@Sun.COM 	} else if (num >= mp->gm_entry_num) {
524*9160SSherry.Moore@Sun.COM 		ent = NULL;
525*9160SSherry.Moore@Sun.COM 	} else {
526*9160SSherry.Moore@Sun.COM 		for (ent = mp->gm_ent_start;
527*9160SSherry.Moore@Sun.COM 		    ent != NULL && num != ent->ge_entry_num;
528*9160SSherry.Moore@Sun.COM 		    ent = ent->ge_next)
529*9160SSherry.Moore@Sun.COM 			;
530*9160SSherry.Moore@Sun.COM 	}
531*9160SSherry.Moore@Sun.COM 	return (ent);
532*9160SSherry.Moore@Sun.COM }
533