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.
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.
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]
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
27 * This file contains functions for manipulating the GRUB menu.
34 #include <sys/types.h>
35 #include <sys/mount.h>
40 #include "libgrub_impl.h"
42 static const grub_cmd_desc_t grub_cmd_descs
[GRBM_CMD_NUM
] = {
43 #define menu_cmd(cmd, num, flag, parsef) {cmd, num, flag},
44 #include "libgrub_cmd.def"
48 append_line(grub_menu_t
*mp
, grub_line_t
*lp
)
50 if (mp
->gm_start
== NULL
) {
53 mp
->gm_end
->gl_next
= lp
;
54 lp
->gl_prev
= mp
->gm_end
;
57 lp
->gl_line_num
= ++mp
->gm_line_num
;
58 lp
->gl_entry_num
= GRUB_ENTRY_DEFAULT
;
62 process_line(grub_menu_t
*mp
)
68 n
= sizeof (grub_cmd_descs
) / sizeof (grub_cmd_descs
[0]);
70 /* search through the table of known commands */
71 while (n
-- != 0 && strcmp(lp
->gl_cmd
, grub_cmd_descs
[n
].gcd_cmd
) != 0)
78 /* we found command, fill lp fields */
79 lp
->gl_flags
= grub_cmd_descs
[n
].gcd_flags
;
80 lp
->gl_cmdtp
= grub_cmd_descs
[n
].gcd_num
;
85 check_entry(grub_entry_t
*ent
)
90 const grub_line_t
* const lend
= ent
->ge_end
->gl_next
;
93 for (i
= 0, lp
= ent
->ge_start
; lend
!= lp
; lp
= lp
->gl_next
, ++i
) {
94 lp
->gl_entry_num
= ent
->ge_entry_num
;
95 if (lp
->gl_flags
== GRUB_LINE_INVALID
||
96 lp
->gl_flags
== GRUB_LINE_GLOBAL
) {
98 lp
->gl_flags
= GRUB_LINE_INVALID
;
102 if ((ent
->ge_emask
= emask
) == 0)
103 ent
->ge_flags
|= GRBM_VALID_FLAG
;
107 add_entry(grub_menu_t
*mp
, grub_line_t
*start
, grub_line_t
*end
)
111 if ((ent
= calloc(1, sizeof (*ent
))) == NULL
)
114 ent
->ge_start
= start
;
117 if (mp
->gm_ent_end
== NULL
) {
118 mp
->gm_ent_start
= ent
;
120 mp
->gm_ent_end
->ge_next
= ent
;
121 ent
->ge_prev
= mp
->gm_ent_end
;
123 mp
->gm_ent_end
= ent
;
124 ent
->ge_entry_num
= mp
->gm_entry_num
++;
130 default_entry(grub_menu_t
*mp
)
137 lp
= mp
->gm_curdefault
;
139 if (lp
!= NULL
&& lp
->gl_flags
== GRUB_LINE_GLOBAL
&&
140 lp
->gl_cmdtp
== GRBM_DEFAULT_CMD
) {
141 defent
= strtoul(lp
->gl_arg
, NULL
, 0);
142 if (defent
>= mp
->gm_entry_num
)
146 for (ent
= mp
->gm_ent_start
; ent
!= NULL
&& defent
!= ent
->ge_entry_num
;
150 mp
->gm_ent_default
= ent
;
154 free_line(grub_line_t
*lp
)
167 free_linelist(grub_line_t
*line
)
182 free_entries(grub_menu_t
*mp
)
184 grub_entry_t
*ent
, *tmp
;
189 for (ent
= mp
->gm_ent_start
; (tmp
= ent
) != NULL
;
190 ent
= tmp
->ge_next
, free(tmp
))
193 mp
->gm_ent_start
= NULL
;
194 mp
->gm_ent_end
= NULL
;
198 grub_menu_append_line(grub_menu_t
*mp
, const char *line
)
209 if ((lp
= calloc(1, sizeof (*lp
))) == NULL
||
210 (lp
->gl_line
= strdup(line
)) == NULL
) {
215 /* skip initial white space */
216 line
+= strspn(line
, " \t");
218 /* process comment line */
219 if (line
[0] == '#') {
221 strdup(grub_cmd_descs
[GRBM_COMMENT_CMD
].gcd_cmd
)) == NULL
||
223 strdup(grub_cmd_descs
[GRBM_EMPTY_CMD
].gcd_cmd
)) == NULL
||
224 (lp
->gl_arg
= strdup(line
+ 1)) == NULL
)
228 n
= strcspn(line
, " \t=");
229 if ((lp
->gl_cmd
= malloc(n
+ 1)) == NULL
)
232 (void) strlcpy(lp
->gl_cmd
, line
, n
+ 1);
237 n
= strspn(line
, " \t=");
238 if ((lp
->gl_sep
= malloc(n
+ 1)) == NULL
)
241 (void) strlcpy(lp
->gl_sep
, line
, n
+ 1);
246 if ((lp
->gl_arg
= strdup(line
)) == NULL
)
261 grub_menu_process(grub_menu_t
*mp
)
265 grub_line_t
*line
, *start
;
267 /* Free remaininig entries, if any */
271 * Walk through lines, till first 'title' command is encountered.
272 * Initialize globals.
274 for (line
= mp
->gm_start
; line
!= NULL
; line
= line
->gl_next
) {
275 if (line
->gl_flags
== GRUB_LINE_GLOBAL
&&
276 line
->gl_cmdtp
== GRBM_DEFAULT_CMD
)
277 mp
->gm_curdefault
= line
;
278 else if (line
->gl_cmdtp
== GRBM_TITLE_CMD
)
283 * Walk through remaining lines and recreate menu entries.
285 for (start
= NULL
; line
!= NULL
; line
= line
->gl_next
) {
286 if (line
->gl_cmdtp
== GRBM_TITLE_CMD
) {
289 (ret
= add_entry(mp
, start
, line
->gl_prev
)) != 0)
296 if (start
!= NULL
&& (ret
= add_entry(mp
, start
, mp
->gm_end
)) != 0)
299 for (ent
= mp
->gm_ent_start
; NULL
!= ent
; ent
= ent
->ge_next
)
308 grub_fs_init(grub_fs_t
*fs
)
311 if ((fs
->gf_lzfh
= libzfs_init()) == NULL
||
312 (fs
->gf_diroot
= di_init("/", DINFOCPYALL
| DINFOPATH
))
314 (fs
->gf_dvlh
= di_devlink_init(NULL
, 0)) == DI_LINK_NIL
) {
321 grub_fs_fini(grub_fs_t
*fs
)
326 if (fs
->gf_dvlh
!= DI_LINK_NIL
)
327 (void) di_devlink_fini(&fs
->gf_dvlh
);
328 if (fs
->gf_diroot
!= DI_NODE_NIL
)
329 di_fini(fs
->gf_diroot
);
330 if (fs
->gf_lzfh
!= NULL
)
331 libzfs_fini(fs
->gf_lzfh
);
332 (void) memset(fs
, 0, sizeof (*fs
));
336 * Reads and parses GRUB menu file into a grub_menu_t data structure.
337 * If grub_menu_path file path is NULL, will use 'currently active'
340 * Memory for the menu data structure is allocated within the routine.
341 * Caller must call grub_menu_fini() to release memory after calling
345 grub_menu_init(const char *path
, grub_menu_t
**menup
)
351 char buf
[GRBM_MAXLINE
];
357 * Allocate space, perform initialization
359 if ((mp
= calloc(1, sizeof (*mp
))) == NULL
) {
364 if ((ret
= grub_fs_init(&mp
->gm_fs
)) != 0 ||
365 (ret
= grub_current_root(&mp
->gm_fs
, &mp
->gm_root
)) != 0)
370 * Use default grub-menu.
371 * If top dataset is not mounted, mount it now.
373 if (mp
->gm_root
.gr_fs
[GRBM_FS_TOP
].gfs_mountp
[0] == 0) {
374 if ((ret
= grub_fsd_mount_tmp(mp
->gm_root
.gr_fs
+
375 GRBM_FS_TOP
, mp
->gm_root
.gr_fstyp
)) != 0)
378 (void) snprintf(mp
->gm_path
, sizeof (mp
->gm_path
),
379 "%s/%s", mp
->gm_root
.gr_fs
[GRBM_FS_TOP
].gfs_mountp
,
382 (void) strlcpy(mp
->gm_path
, path
, sizeof (mp
->gm_path
));
385 if ((fp
= fopen(mp
->gm_path
, "r")) == NULL
) {
393 while (fgets(cp
, len
, fp
) != NULL
) {
395 if (IS_LINE2BIG(cp
, len
, n
)) {
400 /* remove white space at the end of line */
401 for (; n
!= 0 && isspace(cp
[n
- 1]); --n
)
405 if (n
> 0 && cp
[n
- 1] == '\\') {
411 if ((ret
= grub_menu_append_line(mp
, buf
)) != 0)
418 if (fclose(fp
) == EOF
)
421 ret
= grub_menu_process(mp
);
424 grub_fsd_umount_tmp(mp
->gm_root
.gr_fs
+ GRBM_FS_TOP
);
434 grub_menu_fini(grub_menu_t
*mp
)
439 grub_fs_fini(&mp
->gm_fs
);
441 free_linelist(mp
->gm_start
);
446 grub_menu_next_line(const grub_menu_t
*mp
, const grub_line_t
*lp
)
450 return (mp
->gm_start
);
452 return (lp
->gl_next
);
456 grub_menu_prev_line(const grub_menu_t
*mp
, const grub_line_t
*lp
)
462 return (lp
->gl_prev
);
466 grub_menu_get_line(const grub_menu_t
*mp
, int num
)
471 if (num
> mp
->gm_line_num
)
473 for (lp
= mp
->gm_start
; lp
!= NULL
&& num
!= lp
->gl_line_num
;
480 grub_menu_get_cmdline(const grub_menu_t
*mp
, int num
, char *cmdl
, size_t size
)
485 if ((ent
= grub_menu_get_entry(mp
, num
)) == NULL
)
488 return (grub_entry_get_cmdline(ent
, cmdl
, size
));
492 grub_menu_next_entry(const grub_menu_t
*mp
, const grub_entry_t
*ent
)
496 return (mp
->gm_ent_start
);
498 assert(mp
== ent
->ge_menu
);
499 return (ent
->ge_next
);
504 grub_menu_prev_entry(const grub_menu_t
*mp
, const grub_entry_t
*ent
)
508 return (mp
->gm_ent_end
);
510 assert(mp
== ent
->ge_menu
);
511 return (ent
->ge_prev
);
516 grub_menu_get_entry(const grub_menu_t
*mp
, int num
)
521 if (num
== GRUB_ENTRY_DEFAULT
) {
522 ent
= mp
->gm_ent_default
;
523 } else if (num
>= mp
->gm_entry_num
) {
526 for (ent
= mp
->gm_ent_start
;
527 ent
!= NULL
&& num
!= ent
->ge_entry_num
;