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 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * This file contains all the functions that get/set fields
28  * in a GRUB menu entry.
29  */
30 #include <stdio.h>
31 #include <errno.h>
32 #include <stdlib.h>
33 #include <strings.h>
34 #include <unistd.h>
35 #include <sys/types.h>
36 #include <assert.h>
37 #include <ctype.h>
38 
39 #include "libgrub_cmd.def"
40 #include "libgrub_impl.h"
41 
42 typedef int (*barg_parsef_t)(const grub_line_t *, grub_barg_t *);
43 static const  barg_parsef_t barg_parse[] = {
44 #define	menu_cmd(cmd, num, flag, parsef)	parsef,
45 #include "libgrub_cmd.def"
46 };
47 
48 /*
49  * Remove extra '/', stops at first isspace character.
50  * Return new string length.
51  */
52 size_t
clean_path(char * path)53 clean_path(char *path)
54 {
55 	int	i, c;
56 	size_t	k, n;
57 
58 	n = strlen(path) + 1;
59 
60 	for (i = 0; (c = path[i]) != 0 && !isspace(c); i++) {
61 		if (c == '/' && (k = strspn(path + i, "/") - 1) != 0) {
62 			/* bcopy should deal with overlapping buffers */
63 			n -= k;
64 			bcopy(path + i + k, path + i, n - i);
65 		}
66 	}
67 	return (n - 1);
68 }
69 
70 /*
71  * Construct boot command line from the ge_barg field
72  */
73 static size_t
barg_cmdline(const grub_barg_t * barg,char * cmd,size_t size)74 barg_cmdline(const grub_barg_t *barg, char *cmd, size_t size)
75 {
76 	size_t n;
77 	const grub_fsdesc_t *fsd;
78 
79 	if (!IS_BARG_VALID(barg) ||
80 	    (fsd = grub_get_rootfsd(&barg->gb_root)) == NULL)
81 		return ((size_t)-1);
82 
83 	/* if disk/top dataset is mounted, use mount point */
84 	if (fsd->gfs_mountp[0] != 0) {
85 		if ((n = snprintf(cmd, size, "%s%s", fsd->gfs_mountp,
86 		    barg->gb_kernel)) >= size)
87 			return (n);
88 		return (clean_path(cmd));
89 	} else
90 		return (snprintf(cmd, size, "%s %s", fsd->gfs_dev,
91 		    barg->gb_kernel));
92 }
93 
94 
95 /*
96  * Construct ge_barg field based on the other fields of the entry.
97  * Return 0 on success, errno on failure.
98  */
99 int
grub_entry_construct_barg(grub_entry_t * ent)100 grub_entry_construct_barg(grub_entry_t *ent)
101 {
102 	int ret = 0;
103 	grub_barg_t *barg;
104 	grub_line_t *lp, *lend;
105 	grub_menu_t *mp;
106 
107 	assert(ent);
108 
109 	barg = &ent->ge_barg;
110 	mp = ent->ge_menu;
111 
112 	assert(barg);
113 	assert(mp);
114 
115 	(void) memset(barg, 0, sizeof (*barg));
116 	barg->gb_entry = ent;
117 	(void) bcopy(&mp->gm_root, &barg->gb_root, sizeof (barg->gb_root));
118 
119 	lend = ent->ge_end->gl_next;
120 	for (lp = ent->ge_start; lp != lend; lp = lp->gl_next) {
121 		if (lp->gl_cmdtp >= GRBM_CMD_NUM)
122 			ret = EG_INVALIDCMD;
123 		else
124 			ret = barg_parse[lp->gl_cmdtp](lp, barg);
125 
126 		if (ret != 0)
127 			break;
128 	}
129 
130 	barg->gb_errline = lp;
131 	if (ret == 0) {
132 		/* at least kernel and module should be defined */
133 		if (barg->gb_kernel[0] != 0 && barg->gb_module[0] != 0)
134 			barg->gb_flags |= GRBM_VALID_FLAG;
135 	}
136 
137 	return (ret);
138 }
139 
140 const char *
grub_entry_get_fstyp(const grub_entry_t * ent)141 grub_entry_get_fstyp(const grub_entry_t *ent)
142 {
143 	if (IS_ENTRY_BARG_VALID(ent))
144 		return (ent->ge_barg.gb_root.gr_fstyp);
145 	else
146 		return (NULL);
147 }
148 
149 const char *
grub_entry_get_kernel(const grub_entry_t * ent)150 grub_entry_get_kernel(const grub_entry_t *ent)
151 {
152 	if (IS_ENTRY_BARG_VALID(ent))
153 		return (ent->ge_barg.gb_kernel);
154 	else
155 		return (NULL);
156 }
157 
158 const char *
grub_entry_get_module(const grub_entry_t * ent)159 grub_entry_get_module(const grub_entry_t *ent)
160 {
161 	if (IS_ENTRY_BARG_VALID(ent))
162 		return (ent->ge_barg.gb_module);
163 	else
164 		return (NULL);
165 }
166 
167 const char *
grub_entry_get_error_desc(const grub_entry_t * ent)168 grub_entry_get_error_desc(const grub_entry_t *ent)
169 {
170 	assert(ent != NULL);
171 	return ("Not implemented");
172 }
173 
174 const grub_fsdesc_t *
grub_entry_get_rootfs(const grub_entry_t * ent)175 grub_entry_get_rootfs(const grub_entry_t *ent)
176 {
177 	if (IS_ENTRY_BARG_VALID(ent))
178 		return (grub_get_rootfsd(&ent->ge_barg.gb_root));
179 	else
180 		return (NULL);
181 }
182 
183 size_t
grub_entry_get_cmdline(grub_entry_t * ent,char * cmdline,size_t size)184 grub_entry_get_cmdline(grub_entry_t *ent, char *cmdline, size_t size)
185 {
186 	if (IS_ENTRY_VALID(ent) && (grub_entry_construct_barg(ent) == 0))
187 		return (barg_cmdline(&ent->ge_barg, cmdline, size));
188 	else
189 		return ((size_t)-1);
190 
191 }
192