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
42typedef int (*barg_parsef_t)(const grub_line_t *, grub_barg_t *);
43static 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 */
52size_t
53clean_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 */
73static size_t
74barg_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 */
99int
100grub_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
140const char *
141grub_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
149const char *
150grub_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
158const char *
159grub_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
167const char *
168grub_entry_get_error_desc(const grub_entry_t *ent)
169{
170	assert(ent != NULL);
171	return ("Not implemented");
172}
173
174const grub_fsdesc_t *
175grub_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
183size_t
184grub_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