xref: /onnv-gate/usr/src/lib/libgrubmgmt/common/libgrub_menu.c (revision 11002:e2b6058ff058)
19160SSherry.Moore@Sun.COM /*
29160SSherry.Moore@Sun.COM  * CDDL HEADER START
39160SSherry.Moore@Sun.COM  *
49160SSherry.Moore@Sun.COM  * The contents of this file are subject to the terms of the
59160SSherry.Moore@Sun.COM  * Common Development and Distribution License (the "License").
69160SSherry.Moore@Sun.COM  * You may not use this file except in compliance with the License.
79160SSherry.Moore@Sun.COM  *
89160SSherry.Moore@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
99160SSherry.Moore@Sun.COM  * or http://www.opensolaris.org/os/licensing.
109160SSherry.Moore@Sun.COM  * See the License for the specific language governing permissions
119160SSherry.Moore@Sun.COM  * and limitations under the License.
129160SSherry.Moore@Sun.COM  *
139160SSherry.Moore@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
149160SSherry.Moore@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
159160SSherry.Moore@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
169160SSherry.Moore@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
179160SSherry.Moore@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
189160SSherry.Moore@Sun.COM  *
199160SSherry.Moore@Sun.COM  * CDDL HEADER END
209160SSherry.Moore@Sun.COM  */
219160SSherry.Moore@Sun.COM /*
229160SSherry.Moore@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
239160SSherry.Moore@Sun.COM  * Use is subject to license terms.
249160SSherry.Moore@Sun.COM  */
259160SSherry.Moore@Sun.COM 
269160SSherry.Moore@Sun.COM /*
279160SSherry.Moore@Sun.COM  * This file contains functions for manipulating the GRUB menu.
289160SSherry.Moore@Sun.COM  */
299160SSherry.Moore@Sun.COM #include <stdio.h>
309160SSherry.Moore@Sun.COM #include <errno.h>
319160SSherry.Moore@Sun.COM #include <stdlib.h>
329160SSherry.Moore@Sun.COM #include <string.h>
339160SSherry.Moore@Sun.COM #include <unistd.h>
349160SSherry.Moore@Sun.COM #include <sys/types.h>
359160SSherry.Moore@Sun.COM #include <sys/mount.h>
369160SSherry.Moore@Sun.COM #include <stdarg.h>
379160SSherry.Moore@Sun.COM #include <assert.h>
389160SSherry.Moore@Sun.COM #include <ctype.h>
399160SSherry.Moore@Sun.COM 
409160SSherry.Moore@Sun.COM #include "libgrub_impl.h"
419160SSherry.Moore@Sun.COM 
429160SSherry.Moore@Sun.COM static const grub_cmd_desc_t grub_cmd_descs[GRBM_CMD_NUM] = {
439160SSherry.Moore@Sun.COM #define	menu_cmd(cmd, num, flag, parsef)	{cmd, num, flag},
449160SSherry.Moore@Sun.COM #include "libgrub_cmd.def"
459160SSherry.Moore@Sun.COM };
469160SSherry.Moore@Sun.COM 
479160SSherry.Moore@Sun.COM static void
append_line(grub_menu_t * mp,grub_line_t * lp)489160SSherry.Moore@Sun.COM append_line(grub_menu_t *mp, grub_line_t *lp)
499160SSherry.Moore@Sun.COM {
509160SSherry.Moore@Sun.COM 	if (mp->gm_start == NULL) {
519160SSherry.Moore@Sun.COM 		mp->gm_start = lp;
529160SSherry.Moore@Sun.COM 	} else {
539160SSherry.Moore@Sun.COM 		mp->gm_end->gl_next = lp;
549160SSherry.Moore@Sun.COM 		lp->gl_prev = mp->gm_end;
559160SSherry.Moore@Sun.COM 	}
569160SSherry.Moore@Sun.COM 	mp->gm_end = lp;
579160SSherry.Moore@Sun.COM 	lp->gl_line_num = ++mp->gm_line_num;
589160SSherry.Moore@Sun.COM 	lp->gl_entry_num = GRUB_ENTRY_DEFAULT;
599160SSherry.Moore@Sun.COM }
609160SSherry.Moore@Sun.COM 
619160SSherry.Moore@Sun.COM static void
process_line(grub_menu_t * mp)629160SSherry.Moore@Sun.COM process_line(grub_menu_t *mp)
639160SSherry.Moore@Sun.COM {
649160SSherry.Moore@Sun.COM 	int	n;
659160SSherry.Moore@Sun.COM 	grub_line_t	*lp;
669160SSherry.Moore@Sun.COM 
679160SSherry.Moore@Sun.COM 	lp = mp->gm_end;
689160SSherry.Moore@Sun.COM 	n = sizeof (grub_cmd_descs) / sizeof (grub_cmd_descs[0]);
699160SSherry.Moore@Sun.COM 
709160SSherry.Moore@Sun.COM 	/* search through the table of known commands */
719160SSherry.Moore@Sun.COM 	while (n-- != 0 && strcmp(lp->gl_cmd, grub_cmd_descs[n].gcd_cmd) != 0)
729160SSherry.Moore@Sun.COM 		;
739160SSherry.Moore@Sun.COM 
749160SSherry.Moore@Sun.COM 	/* unknown command */
759160SSherry.Moore@Sun.COM 	if (n < 0)
769160SSherry.Moore@Sun.COM 		return;
779160SSherry.Moore@Sun.COM 
789160SSherry.Moore@Sun.COM 	/* we found command, fill lp fields */
799160SSherry.Moore@Sun.COM 	lp->gl_flags = grub_cmd_descs[n].gcd_flags;
809160SSherry.Moore@Sun.COM 	lp->gl_cmdtp = grub_cmd_descs[n].gcd_num;
819160SSherry.Moore@Sun.COM }
829160SSherry.Moore@Sun.COM 
839160SSherry.Moore@Sun.COM 
849160SSherry.Moore@Sun.COM static void
check_entry(grub_entry_t * ent)859160SSherry.Moore@Sun.COM check_entry(grub_entry_t *ent)
869160SSherry.Moore@Sun.COM {
879160SSherry.Moore@Sun.COM 	int i;
889160SSherry.Moore@Sun.COM 	uint_t emask;
899160SSherry.Moore@Sun.COM 	grub_line_t *lp;
909160SSherry.Moore@Sun.COM 	const grub_line_t * const lend = ent->ge_end->gl_next;
919160SSherry.Moore@Sun.COM 
929160SSherry.Moore@Sun.COM 	emask = 0;
939160SSherry.Moore@Sun.COM 	for (i = 0, lp = ent->ge_start; lend != lp; lp = lp->gl_next, ++i) {
949160SSherry.Moore@Sun.COM 		lp->gl_entry_num = ent->ge_entry_num;
959160SSherry.Moore@Sun.COM 		if (lp->gl_flags == GRUB_LINE_INVALID ||
969160SSherry.Moore@Sun.COM 		    lp->gl_flags == GRUB_LINE_GLOBAL) {
979160SSherry.Moore@Sun.COM 			emask |= 1 << i;
989160SSherry.Moore@Sun.COM 			lp->gl_flags = GRUB_LINE_INVALID;
999160SSherry.Moore@Sun.COM 		}
1009160SSherry.Moore@Sun.COM 	}
1019160SSherry.Moore@Sun.COM 
1029160SSherry.Moore@Sun.COM 	if ((ent->ge_emask = emask) == 0)
1039160SSherry.Moore@Sun.COM 		ent->ge_flags |= GRBM_VALID_FLAG;
1049160SSherry.Moore@Sun.COM }
1059160SSherry.Moore@Sun.COM 
1069160SSherry.Moore@Sun.COM static int
add_entry(grub_menu_t * mp,grub_line_t * start,grub_line_t * end)1079160SSherry.Moore@Sun.COM add_entry(grub_menu_t *mp, grub_line_t *start, grub_line_t *end)
1089160SSherry.Moore@Sun.COM {
1099160SSherry.Moore@Sun.COM 	grub_entry_t *ent;
1109160SSherry.Moore@Sun.COM 
1119160SSherry.Moore@Sun.COM 	if ((ent = calloc(1, sizeof (*ent))) == NULL)
1129160SSherry.Moore@Sun.COM 		return (errno);
1139160SSherry.Moore@Sun.COM 
1149160SSherry.Moore@Sun.COM 	ent->ge_start = start;
1159160SSherry.Moore@Sun.COM 	ent->ge_end = end;
1169160SSherry.Moore@Sun.COM 
1179160SSherry.Moore@Sun.COM 	if (mp->gm_ent_end == NULL) {
1189160SSherry.Moore@Sun.COM 		mp->gm_ent_start = ent;
1199160SSherry.Moore@Sun.COM 	} else {
1209160SSherry.Moore@Sun.COM 		mp->gm_ent_end->ge_next = ent;
1219160SSherry.Moore@Sun.COM 		ent->ge_prev = mp->gm_ent_end;
1229160SSherry.Moore@Sun.COM 	}
1239160SSherry.Moore@Sun.COM 	mp->gm_ent_end = ent;
1249160SSherry.Moore@Sun.COM 	ent->ge_entry_num = mp->gm_entry_num++;
1259160SSherry.Moore@Sun.COM 	ent->ge_menu = mp;
1269160SSherry.Moore@Sun.COM 	return (0);
1279160SSherry.Moore@Sun.COM }
1289160SSherry.Moore@Sun.COM 
1299160SSherry.Moore@Sun.COM static void
default_entry(grub_menu_t * mp)1309160SSherry.Moore@Sun.COM default_entry(grub_menu_t *mp)
1319160SSherry.Moore@Sun.COM {
1329160SSherry.Moore@Sun.COM 	uint_t defent;
1339160SSherry.Moore@Sun.COM 	grub_line_t *lp;
1349160SSherry.Moore@Sun.COM 	grub_entry_t *ent;
1359160SSherry.Moore@Sun.COM 
1369160SSherry.Moore@Sun.COM 	defent = 0;
1379160SSherry.Moore@Sun.COM 	lp = mp->gm_curdefault;
1389160SSherry.Moore@Sun.COM 
1399160SSherry.Moore@Sun.COM 	if (lp != NULL && lp->gl_flags == GRUB_LINE_GLOBAL &&
1409160SSherry.Moore@Sun.COM 	    lp->gl_cmdtp == GRBM_DEFAULT_CMD) {
1419160SSherry.Moore@Sun.COM 		defent  = strtoul(lp->gl_arg, NULL, 0);
1429160SSherry.Moore@Sun.COM 		if (defent >= mp->gm_entry_num)
1439160SSherry.Moore@Sun.COM 			defent = 0;
1449160SSherry.Moore@Sun.COM 	}
1459160SSherry.Moore@Sun.COM 
1469160SSherry.Moore@Sun.COM 	for (ent = mp->gm_ent_start; ent != NULL && defent != ent->ge_entry_num;
1479160SSherry.Moore@Sun.COM 	    ent = ent->ge_next)
1489160SSherry.Moore@Sun.COM 		;
1499160SSherry.Moore@Sun.COM 
1509160SSherry.Moore@Sun.COM 	mp->gm_ent_default = ent;
1519160SSherry.Moore@Sun.COM }
1529160SSherry.Moore@Sun.COM 
1539160SSherry.Moore@Sun.COM static void
free_line(grub_line_t * lp)1549160SSherry.Moore@Sun.COM free_line(grub_line_t *lp)
1559160SSherry.Moore@Sun.COM {
1569160SSherry.Moore@Sun.COM 	if (lp == NULL)
1579160SSherry.Moore@Sun.COM 		return;
1589160SSherry.Moore@Sun.COM 
1599160SSherry.Moore@Sun.COM 	free(lp->gl_cmd);
1609160SSherry.Moore@Sun.COM 	free(lp->gl_sep);
1619160SSherry.Moore@Sun.COM 	free(lp->gl_arg);
1629160SSherry.Moore@Sun.COM 	free(lp->gl_line);
1639160SSherry.Moore@Sun.COM 	free(lp);
1649160SSherry.Moore@Sun.COM }
1659160SSherry.Moore@Sun.COM 
1669160SSherry.Moore@Sun.COM static void
free_linelist(grub_line_t * line)1679160SSherry.Moore@Sun.COM free_linelist(grub_line_t *line)
1689160SSherry.Moore@Sun.COM {
1699160SSherry.Moore@Sun.COM 	grub_line_t *lp;
1709160SSherry.Moore@Sun.COM 
1719160SSherry.Moore@Sun.COM 	if (line == NULL)
1729160SSherry.Moore@Sun.COM 		return;
1739160SSherry.Moore@Sun.COM 
1749160SSherry.Moore@Sun.COM 	while (line) {
1759160SSherry.Moore@Sun.COM 		lp = line;
1769160SSherry.Moore@Sun.COM 		line = lp->gl_next;
1779160SSherry.Moore@Sun.COM 		free_line(lp);
1789160SSherry.Moore@Sun.COM 	}
1799160SSherry.Moore@Sun.COM }
1809160SSherry.Moore@Sun.COM 
1819160SSherry.Moore@Sun.COM static void
free_entries(grub_menu_t * mp)1829160SSherry.Moore@Sun.COM free_entries(grub_menu_t *mp)
1839160SSherry.Moore@Sun.COM {
1849160SSherry.Moore@Sun.COM 	grub_entry_t *ent, *tmp;
1859160SSherry.Moore@Sun.COM 
1869160SSherry.Moore@Sun.COM 	if (mp == NULL)
1879160SSherry.Moore@Sun.COM 		return;
1889160SSherry.Moore@Sun.COM 
1899160SSherry.Moore@Sun.COM 	for (ent = mp->gm_ent_start; (tmp = ent) != NULL;
1909160SSherry.Moore@Sun.COM 	    ent = tmp->ge_next, free(tmp))
1919160SSherry.Moore@Sun.COM 		;
1929160SSherry.Moore@Sun.COM 
1939160SSherry.Moore@Sun.COM 	mp->gm_ent_start = NULL;
1949160SSherry.Moore@Sun.COM 	mp->gm_ent_end = NULL;
1959160SSherry.Moore@Sun.COM }
1969160SSherry.Moore@Sun.COM 
1979160SSherry.Moore@Sun.COM static int
grub_menu_append_line(grub_menu_t * mp,const char * line)1989160SSherry.Moore@Sun.COM grub_menu_append_line(grub_menu_t *mp, const char *line)
1999160SSherry.Moore@Sun.COM {
2009160SSherry.Moore@Sun.COM 	int rc;
2019160SSherry.Moore@Sun.COM 	size_t n;
2029160SSherry.Moore@Sun.COM 	grub_line_t *lp;
2039160SSherry.Moore@Sun.COM 
2049160SSherry.Moore@Sun.COM 	if (line == NULL)
2059160SSherry.Moore@Sun.COM 		return (EINVAL);
2069160SSherry.Moore@Sun.COM 
2079160SSherry.Moore@Sun.COM 	rc = 0;
2089160SSherry.Moore@Sun.COM 	lp = NULL;
2099160SSherry.Moore@Sun.COM 	if ((lp = calloc(1, sizeof (*lp))) == NULL ||
2109160SSherry.Moore@Sun.COM 	    (lp->gl_line = strdup(line)) == NULL) {
2119160SSherry.Moore@Sun.COM 		free(lp);
2129160SSherry.Moore@Sun.COM 		return (errno);
2139160SSherry.Moore@Sun.COM 	}
2149160SSherry.Moore@Sun.COM 
2159160SSherry.Moore@Sun.COM 	/* skip initial white space */
2169160SSherry.Moore@Sun.COM 	line += strspn(line, " \t");
2179160SSherry.Moore@Sun.COM 
2189160SSherry.Moore@Sun.COM 	/* process comment line */
2199160SSherry.Moore@Sun.COM 	if (line[0] == '#') {
2209160SSherry.Moore@Sun.COM 		if ((lp->gl_cmd =
2219160SSherry.Moore@Sun.COM 		    strdup(grub_cmd_descs[GRBM_COMMENT_CMD].gcd_cmd)) == NULL ||
2229160SSherry.Moore@Sun.COM 		    (lp->gl_sep =
2239160SSherry.Moore@Sun.COM 		    strdup(grub_cmd_descs[GRBM_EMPTY_CMD].gcd_cmd)) == NULL ||
2249160SSherry.Moore@Sun.COM 		    (lp->gl_arg = strdup(line + 1)) == NULL)
2259160SSherry.Moore@Sun.COM 			rc = errno;
2269160SSherry.Moore@Sun.COM 	} else {
2279160SSherry.Moore@Sun.COM 		/* get command */
2289160SSherry.Moore@Sun.COM 		n = strcspn(line, " \t=");
2299160SSherry.Moore@Sun.COM 		if ((lp->gl_cmd = malloc(n + 1)) == NULL)
2309160SSherry.Moore@Sun.COM 			rc = errno;
2319160SSherry.Moore@Sun.COM 		else
2329160SSherry.Moore@Sun.COM 			(void) strlcpy(lp->gl_cmd, line, n + 1);
2339160SSherry.Moore@Sun.COM 
2349160SSherry.Moore@Sun.COM 		line += n;
2359160SSherry.Moore@Sun.COM 
2369160SSherry.Moore@Sun.COM 		/* get separator */
2379160SSherry.Moore@Sun.COM 		n = strspn(line, " \t=");
2389160SSherry.Moore@Sun.COM 		if ((lp->gl_sep = malloc(n + 1)) == NULL)
2399160SSherry.Moore@Sun.COM 			rc = errno;
2409160SSherry.Moore@Sun.COM 		else
2419160SSherry.Moore@Sun.COM 			(void) strlcpy(lp->gl_sep, line, n + 1);
2429160SSherry.Moore@Sun.COM 
2439160SSherry.Moore@Sun.COM 		line += n;
2449160SSherry.Moore@Sun.COM 
2459160SSherry.Moore@Sun.COM 		/* get arguments */
2469160SSherry.Moore@Sun.COM 		if ((lp->gl_arg = strdup(line)) == NULL)
2479160SSherry.Moore@Sun.COM 			rc = errno;
2489160SSherry.Moore@Sun.COM 	}
2499160SSherry.Moore@Sun.COM 
2509160SSherry.Moore@Sun.COM 	if (rc != 0) {
2519160SSherry.Moore@Sun.COM 		free_line(lp);
2529160SSherry.Moore@Sun.COM 		return (rc);
2539160SSherry.Moore@Sun.COM 	}
2549160SSherry.Moore@Sun.COM 
2559160SSherry.Moore@Sun.COM 	append_line(mp, lp);
2569160SSherry.Moore@Sun.COM 	process_line(mp);
2579160SSherry.Moore@Sun.COM 	return (0);
2589160SSherry.Moore@Sun.COM }
2599160SSherry.Moore@Sun.COM 
2609160SSherry.Moore@Sun.COM static int
grub_menu_process(grub_menu_t * mp)2619160SSherry.Moore@Sun.COM grub_menu_process(grub_menu_t *mp)
2629160SSherry.Moore@Sun.COM {
2639160SSherry.Moore@Sun.COM 	int ret;
2649160SSherry.Moore@Sun.COM 	grub_entry_t *ent;
2659160SSherry.Moore@Sun.COM 	grub_line_t *line, *start;
2669160SSherry.Moore@Sun.COM 
2679160SSherry.Moore@Sun.COM 	/* Free remaininig entries, if any */
2689160SSherry.Moore@Sun.COM 	free_entries(mp);
2699160SSherry.Moore@Sun.COM 
2709160SSherry.Moore@Sun.COM 	/*
2719160SSherry.Moore@Sun.COM 	 * Walk through lines, till first 'title' command is encountered.
2729160SSherry.Moore@Sun.COM 	 * Initialize globals.
2739160SSherry.Moore@Sun.COM 	 */
2749160SSherry.Moore@Sun.COM 	for (line = mp->gm_start; line != NULL; line = line->gl_next) {
2759160SSherry.Moore@Sun.COM 		if (line->gl_flags == GRUB_LINE_GLOBAL &&
2769160SSherry.Moore@Sun.COM 		    line->gl_cmdtp == GRBM_DEFAULT_CMD)
2779160SSherry.Moore@Sun.COM 			mp->gm_curdefault = line;
2789160SSherry.Moore@Sun.COM 		else if (line->gl_cmdtp == GRBM_TITLE_CMD)
2799160SSherry.Moore@Sun.COM 			break;
2809160SSherry.Moore@Sun.COM 	}
2819160SSherry.Moore@Sun.COM 
2829160SSherry.Moore@Sun.COM 	/*
2839160SSherry.Moore@Sun.COM 	 * Walk through remaining lines and recreate menu entries.
2849160SSherry.Moore@Sun.COM 	 */
2859160SSherry.Moore@Sun.COM 	for (start = NULL; line != NULL; line = line->gl_next) {
2869160SSherry.Moore@Sun.COM 		if (line->gl_cmdtp == GRBM_TITLE_CMD) {
2879160SSherry.Moore@Sun.COM 			/* is first entry */
2889160SSherry.Moore@Sun.COM 			if (start != NULL &&
2899160SSherry.Moore@Sun.COM 			    (ret = add_entry(mp, start, line->gl_prev)) != 0)
2909160SSherry.Moore@Sun.COM 				return (ret);
2919160SSherry.Moore@Sun.COM 			start = line;
2929160SSherry.Moore@Sun.COM 		}
2939160SSherry.Moore@Sun.COM 	}
2949160SSherry.Moore@Sun.COM 
2959160SSherry.Moore@Sun.COM 	/* Add last entry */
2969160SSherry.Moore@Sun.COM 	if (start != NULL && (ret = add_entry(mp, start, mp->gm_end)) != 0)
2979160SSherry.Moore@Sun.COM 		return (ret);
2989160SSherry.Moore@Sun.COM 
2999160SSherry.Moore@Sun.COM 	for (ent = mp->gm_ent_start; NULL != ent; ent = ent->ge_next)
3009160SSherry.Moore@Sun.COM 		check_entry(ent);
3019160SSherry.Moore@Sun.COM 
3029160SSherry.Moore@Sun.COM 	default_entry(mp);
3039160SSherry.Moore@Sun.COM 
3049160SSherry.Moore@Sun.COM 	return (0);
3059160SSherry.Moore@Sun.COM }
3069160SSherry.Moore@Sun.COM 
3079160SSherry.Moore@Sun.COM static int
grub_fs_init(grub_fs_t * fs)3089160SSherry.Moore@Sun.COM grub_fs_init(grub_fs_t *fs)
3099160SSherry.Moore@Sun.COM {
3109160SSherry.Moore@Sun.COM 	assert(fs);
3119160SSherry.Moore@Sun.COM 	if ((fs->gf_lzfh = libzfs_init()) == NULL ||
3129160SSherry.Moore@Sun.COM 	    (fs->gf_diroot = di_init("/", DINFOCPYALL | DINFOPATH))
3139160SSherry.Moore@Sun.COM 	    == DI_NODE_NIL ||
3149160SSherry.Moore@Sun.COM 	    (fs->gf_dvlh = di_devlink_init(NULL, 0)) == DI_LINK_NIL) {
3159160SSherry.Moore@Sun.COM 		return (EG_INITFS);
3169160SSherry.Moore@Sun.COM 	}
3179160SSherry.Moore@Sun.COM 	return (0);
3189160SSherry.Moore@Sun.COM }
3199160SSherry.Moore@Sun.COM 
3209160SSherry.Moore@Sun.COM static void
grub_fs_fini(grub_fs_t * fs)3219160SSherry.Moore@Sun.COM grub_fs_fini(grub_fs_t *fs)
3229160SSherry.Moore@Sun.COM {
3239160SSherry.Moore@Sun.COM 	if (fs == NULL)
3249160SSherry.Moore@Sun.COM 		return;
3259160SSherry.Moore@Sun.COM 
3269160SSherry.Moore@Sun.COM 	if (fs->gf_dvlh != DI_LINK_NIL)
3279160SSherry.Moore@Sun.COM 		(void) di_devlink_fini(&fs->gf_dvlh);
3289160SSherry.Moore@Sun.COM 	if (fs->gf_diroot != DI_NODE_NIL)
3299160SSherry.Moore@Sun.COM 		di_fini(fs->gf_diroot);
3309160SSherry.Moore@Sun.COM 	if (fs->gf_lzfh != NULL)
3319160SSherry.Moore@Sun.COM 		libzfs_fini(fs->gf_lzfh);
3329160SSherry.Moore@Sun.COM 	(void) memset(fs, 0, sizeof (*fs));
3339160SSherry.Moore@Sun.COM }
3349160SSherry.Moore@Sun.COM 
3359160SSherry.Moore@Sun.COM /*
3369160SSherry.Moore@Sun.COM  * Reads and parses GRUB menu file into a grub_menu_t data structure.
3379160SSherry.Moore@Sun.COM  * If grub_menu_path file path is NULL, will use 'currently active'
3389160SSherry.Moore@Sun.COM  * GRUB menu file.
3399160SSherry.Moore@Sun.COM  *
3409160SSherry.Moore@Sun.COM  * Memory for the menu data structure is allocated within the routine.
3419160SSherry.Moore@Sun.COM  * Caller must call grub_menu_fini() to release memory after calling
3429160SSherry.Moore@Sun.COM  * grub_menu_init().
3439160SSherry.Moore@Sun.COM  */
3449160SSherry.Moore@Sun.COM int
grub_menu_init(const char * path,grub_menu_t ** menup)3459160SSherry.Moore@Sun.COM grub_menu_init(const char *path, grub_menu_t **menup)
3469160SSherry.Moore@Sun.COM {
3479160SSherry.Moore@Sun.COM 	FILE *fp;
3489160SSherry.Moore@Sun.COM 	char *cp;
3499160SSherry.Moore@Sun.COM 	grub_menu_t *mp;
3509160SSherry.Moore@Sun.COM 	int len, n, ret;
3519160SSherry.Moore@Sun.COM 	char buf[GRBM_MAXLINE];
3529160SSherry.Moore@Sun.COM 
3539160SSherry.Moore@Sun.COM 	if (menup == NULL)
3549160SSherry.Moore@Sun.COM 		return (EINVAL);
3559160SSherry.Moore@Sun.COM 
3569160SSherry.Moore@Sun.COM 	/*
3579160SSherry.Moore@Sun.COM 	 * Allocate space, perform initialization
3589160SSherry.Moore@Sun.COM 	 */
3599160SSherry.Moore@Sun.COM 	if ((mp = calloc(1, sizeof (*mp))) == NULL) {
3609160SSherry.Moore@Sun.COM 		*menup = mp;
3619160SSherry.Moore@Sun.COM 		return (errno);
3629160SSherry.Moore@Sun.COM 	}
3639160SSherry.Moore@Sun.COM 
3649160SSherry.Moore@Sun.COM 	if ((ret = grub_fs_init(&mp->gm_fs)) != 0 ||
3659160SSherry.Moore@Sun.COM 	    (ret = grub_current_root(&mp->gm_fs, &mp->gm_root)) != 0)
3669160SSherry.Moore@Sun.COM 		goto err_out1;
3679160SSherry.Moore@Sun.COM 
3689160SSherry.Moore@Sun.COM 	if (path == NULL) {
3699160SSherry.Moore@Sun.COM 		/*
3709160SSherry.Moore@Sun.COM 		 * Use default grub-menu.
3719160SSherry.Moore@Sun.COM 		 * If top dataset is not mounted, mount it now.
3729160SSherry.Moore@Sun.COM 		 */
3739160SSherry.Moore@Sun.COM 		if (mp->gm_root.gr_fs[GRBM_FS_TOP].gfs_mountp[0] == 0) {
3749160SSherry.Moore@Sun.COM 			if ((ret = grub_fsd_mount_tmp(mp->gm_root.gr_fs +
3759160SSherry.Moore@Sun.COM 			    GRBM_FS_TOP, mp->gm_root.gr_fstyp)) != 0)
3769160SSherry.Moore@Sun.COM 				goto err_out1;
3779160SSherry.Moore@Sun.COM 		}
3789160SSherry.Moore@Sun.COM 		(void) snprintf(mp->gm_path, sizeof (mp->gm_path),
3799160SSherry.Moore@Sun.COM 		    "%s/%s", mp->gm_root.gr_fs[GRBM_FS_TOP].gfs_mountp,
3809160SSherry.Moore@Sun.COM 		    GRUB_MENU);
3819160SSherry.Moore@Sun.COM 	} else {
3829160SSherry.Moore@Sun.COM 		(void) strlcpy(mp->gm_path, path, sizeof (mp->gm_path));
3839160SSherry.Moore@Sun.COM 	}
3849160SSherry.Moore@Sun.COM 
3859160SSherry.Moore@Sun.COM 	if ((fp = fopen(mp->gm_path, "r")) == NULL) {
3869160SSherry.Moore@Sun.COM 		ret = errno;
3879160SSherry.Moore@Sun.COM 		goto err_out1;
3889160SSherry.Moore@Sun.COM 	}
3899160SSherry.Moore@Sun.COM 
3909160SSherry.Moore@Sun.COM 	cp = buf;
3919160SSherry.Moore@Sun.COM 	len = sizeof (buf);
3929160SSherry.Moore@Sun.COM 
3939160SSherry.Moore@Sun.COM 	while (fgets(cp, len, fp) != NULL) {
3949160SSherry.Moore@Sun.COM 
3959160SSherry.Moore@Sun.COM 		if (IS_LINE2BIG(cp, len, n)) {
3969160SSherry.Moore@Sun.COM 			ret = E2BIG;
3979160SSherry.Moore@Sun.COM 			break;
3989160SSherry.Moore@Sun.COM 		}
3999160SSherry.Moore@Sun.COM 
4009160SSherry.Moore@Sun.COM 		/* remove white space at the end of line */
401*11002SKonstantin.Ananyev@Sun.COM 		for (; n != 0 && isspace(cp[n - 1]); --n)
4029160SSherry.Moore@Sun.COM 			;
4039160SSherry.Moore@Sun.COM 		cp[n] = '\0';
4049160SSherry.Moore@Sun.COM 
405*11002SKonstantin.Ananyev@Sun.COM 		if (n > 0 && cp[n - 1] == '\\') {
4069160SSherry.Moore@Sun.COM 			len -= n - 1;
4079160SSherry.Moore@Sun.COM 			assert(len >= 2);
4089160SSherry.Moore@Sun.COM 			cp += n - 1;
4099160SSherry.Moore@Sun.COM 			continue;
4109160SSherry.Moore@Sun.COM 		}
4119160SSherry.Moore@Sun.COM 		if ((ret = grub_menu_append_line(mp, buf)) != 0)
4129160SSherry.Moore@Sun.COM 			break;
4139160SSherry.Moore@Sun.COM 
4149160SSherry.Moore@Sun.COM 		cp = buf;
4159160SSherry.Moore@Sun.COM 		len = sizeof (buf);
4169160SSherry.Moore@Sun.COM 	}
4179160SSherry.Moore@Sun.COM 
4189160SSherry.Moore@Sun.COM 	if (fclose(fp) == EOF)
4199160SSherry.Moore@Sun.COM 		ret = errno;
4209160SSherry.Moore@Sun.COM 	else if (ret == 0)
4219160SSherry.Moore@Sun.COM 		ret = grub_menu_process(mp);
4229160SSherry.Moore@Sun.COM 
4239160SSherry.Moore@Sun.COM err_out1:
4249160SSherry.Moore@Sun.COM 	grub_fsd_umount_tmp(mp->gm_root.gr_fs + GRBM_FS_TOP);
4259160SSherry.Moore@Sun.COM 	if (0 != ret) {
4269160SSherry.Moore@Sun.COM 		grub_menu_fini(mp);
4279160SSherry.Moore@Sun.COM 		mp = NULL;
4289160SSherry.Moore@Sun.COM 	}
4299160SSherry.Moore@Sun.COM 	*menup = mp;
4309160SSherry.Moore@Sun.COM 	return (ret);
4319160SSherry.Moore@Sun.COM }
4329160SSherry.Moore@Sun.COM 
4339160SSherry.Moore@Sun.COM void
grub_menu_fini(grub_menu_t * mp)4349160SSherry.Moore@Sun.COM grub_menu_fini(grub_menu_t *mp)
4359160SSherry.Moore@Sun.COM {
4369160SSherry.Moore@Sun.COM 	if (mp == NULL)
4379160SSherry.Moore@Sun.COM 		return;
4389160SSherry.Moore@Sun.COM 
4399160SSherry.Moore@Sun.COM 	grub_fs_fini(&mp->gm_fs);
4409160SSherry.Moore@Sun.COM 	free_entries(mp);
4419160SSherry.Moore@Sun.COM 	free_linelist(mp->gm_start);
4429160SSherry.Moore@Sun.COM 	free(mp);
4439160SSherry.Moore@Sun.COM }
4449160SSherry.Moore@Sun.COM 
4459160SSherry.Moore@Sun.COM grub_line_t *
grub_menu_next_line(const grub_menu_t * mp,const grub_line_t * lp)4469160SSherry.Moore@Sun.COM grub_menu_next_line(const grub_menu_t *mp, const grub_line_t *lp)
4479160SSherry.Moore@Sun.COM {
4489160SSherry.Moore@Sun.COM 	assert(mp);
4499160SSherry.Moore@Sun.COM 	if (lp == NULL)
4509160SSherry.Moore@Sun.COM 		return (mp->gm_start);
4519160SSherry.Moore@Sun.COM 	else
4529160SSherry.Moore@Sun.COM 		return (lp->gl_next);
4539160SSherry.Moore@Sun.COM }
4549160SSherry.Moore@Sun.COM 
4559160SSherry.Moore@Sun.COM grub_line_t *
grub_menu_prev_line(const grub_menu_t * mp,const grub_line_t * lp)4569160SSherry.Moore@Sun.COM grub_menu_prev_line(const grub_menu_t *mp, const grub_line_t *lp)
4579160SSherry.Moore@Sun.COM {
4589160SSherry.Moore@Sun.COM 	assert(mp);
4599160SSherry.Moore@Sun.COM 	if (lp == NULL)
4609160SSherry.Moore@Sun.COM 		return (mp->gm_end);
4619160SSherry.Moore@Sun.COM 	else
4629160SSherry.Moore@Sun.COM 		return (lp->gl_prev);
4639160SSherry.Moore@Sun.COM }
4649160SSherry.Moore@Sun.COM 
4659160SSherry.Moore@Sun.COM grub_line_t *
grub_menu_get_line(const grub_menu_t * mp,int num)4669160SSherry.Moore@Sun.COM grub_menu_get_line(const grub_menu_t *mp, int num)
4679160SSherry.Moore@Sun.COM {
4689160SSherry.Moore@Sun.COM 	grub_line_t *lp;
4699160SSherry.Moore@Sun.COM 
4709160SSherry.Moore@Sun.COM 	assert(mp);
4719160SSherry.Moore@Sun.COM 	if (num > mp->gm_line_num)
4729160SSherry.Moore@Sun.COM 		return (NULL);
4739160SSherry.Moore@Sun.COM 	for (lp = mp->gm_start; lp != NULL && num != lp->gl_line_num;
4749160SSherry.Moore@Sun.COM 	    lp = lp->gl_next)
4759160SSherry.Moore@Sun.COM 		;
4769160SSherry.Moore@Sun.COM 	return (lp);
4779160SSherry.Moore@Sun.COM }
4789160SSherry.Moore@Sun.COM 
4799160SSherry.Moore@Sun.COM size_t
grub_menu_get_cmdline(const grub_menu_t * mp,int num,char * cmdl,size_t size)4809160SSherry.Moore@Sun.COM grub_menu_get_cmdline(const grub_menu_t *mp, int num, char *cmdl, size_t size)
4819160SSherry.Moore@Sun.COM {
4829160SSherry.Moore@Sun.COM 	grub_entry_t *ent;
4839160SSherry.Moore@Sun.COM 
4849160SSherry.Moore@Sun.COM 	assert(mp);
4859160SSherry.Moore@Sun.COM 	if ((ent = grub_menu_get_entry(mp, num)) == NULL)
4869160SSherry.Moore@Sun.COM 		return (size_t)(-1);
4879160SSherry.Moore@Sun.COM 
4889160SSherry.Moore@Sun.COM 	return (grub_entry_get_cmdline(ent, cmdl, size));
4899160SSherry.Moore@Sun.COM }
4909160SSherry.Moore@Sun.COM 
4919160SSherry.Moore@Sun.COM grub_entry_t *
grub_menu_next_entry(const grub_menu_t * mp,const grub_entry_t * ent)4929160SSherry.Moore@Sun.COM grub_menu_next_entry(const grub_menu_t *mp, const grub_entry_t *ent)
4939160SSherry.Moore@Sun.COM {
4949160SSherry.Moore@Sun.COM 	assert(mp);
4959160SSherry.Moore@Sun.COM 	if (ent == NULL) {
4969160SSherry.Moore@Sun.COM 		return (mp->gm_ent_start);
4979160SSherry.Moore@Sun.COM 	} else {
4989160SSherry.Moore@Sun.COM 		assert(mp == ent->ge_menu);
4999160SSherry.Moore@Sun.COM 		return (ent->ge_next);
5009160SSherry.Moore@Sun.COM 	}
5019160SSherry.Moore@Sun.COM }
5029160SSherry.Moore@Sun.COM 
5039160SSherry.Moore@Sun.COM grub_entry_t *
grub_menu_prev_entry(const grub_menu_t * mp,const grub_entry_t * ent)5049160SSherry.Moore@Sun.COM grub_menu_prev_entry(const grub_menu_t *mp, const grub_entry_t *ent)
5059160SSherry.Moore@Sun.COM {
5069160SSherry.Moore@Sun.COM 	assert(mp);
5079160SSherry.Moore@Sun.COM 	if (ent == NULL) {
5089160SSherry.Moore@Sun.COM 		return (mp->gm_ent_end);
5099160SSherry.Moore@Sun.COM 	} else {
5109160SSherry.Moore@Sun.COM 		assert(mp == ent->ge_menu);
5119160SSherry.Moore@Sun.COM 		return (ent->ge_prev);
5129160SSherry.Moore@Sun.COM 	}
5139160SSherry.Moore@Sun.COM }
5149160SSherry.Moore@Sun.COM 
5159160SSherry.Moore@Sun.COM grub_entry_t *
grub_menu_get_entry(const grub_menu_t * mp,int num)5169160SSherry.Moore@Sun.COM grub_menu_get_entry(const grub_menu_t *mp, int num)
5179160SSherry.Moore@Sun.COM {
5189160SSherry.Moore@Sun.COM 	grub_entry_t *ent;
5199160SSherry.Moore@Sun.COM 
5209160SSherry.Moore@Sun.COM 	assert(mp);
5219160SSherry.Moore@Sun.COM 	if (num == GRUB_ENTRY_DEFAULT) {
5229160SSherry.Moore@Sun.COM 		ent = mp->gm_ent_default;
5239160SSherry.Moore@Sun.COM 	} else if (num >= mp->gm_entry_num) {
5249160SSherry.Moore@Sun.COM 		ent = NULL;
5259160SSherry.Moore@Sun.COM 	} else {
5269160SSherry.Moore@Sun.COM 		for (ent = mp->gm_ent_start;
5279160SSherry.Moore@Sun.COM 		    ent != NULL && num != ent->ge_entry_num;
5289160SSherry.Moore@Sun.COM 		    ent = ent->ge_next)
5299160SSherry.Moore@Sun.COM 			;
5309160SSherry.Moore@Sun.COM 	}
5319160SSherry.Moore@Sun.COM 	return (ent);
5329160SSherry.Moore@Sun.COM }
533