Home | History | Annotate | Download | only in bootadm
      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 #include <stdio.h>
     27 #include <errno.h>
     28 #include <stdlib.h>
     29 #include <string.h>
     30 #include <unistd.h>
     31 #include <sys/types.h>
     32 #include <sys/stat.h>
     33 #include <limits.h>
     34 #include <fcntl.h>
     35 #include <strings.h>
     36 
     37 #include <sys/mman.h>
     38 #include <sys/elf.h>
     39 #include <sys/multiboot.h>
     40 
     41 #include "message.h"
     42 #include "bootadm.h"
     43 
     44 direct_or_multi_t bam_direct = BAM_DIRECT_NOT_SET;
     45 hv_t bam_is_hv = BAM_HV_UNKNOWN;
     46 findroot_t bam_is_findroot = BAM_FINDROOT_UNKNOWN;
     47 
     48 static void
     49 get_findroot_cap(const char *osroot)
     50 {
     51 	FILE		*fp;
     52 	char		path[PATH_MAX];
     53 	char		buf[BAM_MAXLINE];
     54 	struct stat	sb;
     55 	int		dboot;
     56 	int		error;
     57 	int		ret;
     58 	const char	*fcn = "get_findroot_cap()";
     59 
     60 	assert(is_grub(osroot));
     61 
     62 	(void) snprintf(path, sizeof (path), "%s/%s",
     63 	    osroot, "boot/grub/capability");
     64 
     65 	if (stat(path, &sb) == -1) {
     66 		bam_is_findroot = BAM_FINDROOT_ABSENT;
     67 		BAM_DPRINTF((D_FINDROOT_ABSENT, fcn));
     68 		return;
     69 	}
     70 
     71 	fp = fopen(path, "r");
     72 	error = errno;
     73 	INJECT_ERROR1("GET_CAP_FINDROOT_FOPEN", fp = NULL);
     74 	if (fp == NULL) {
     75 		bam_error(OPEN_FAIL, path, strerror(error));
     76 		return;
     77 	}
     78 
     79 	dboot = 0;
     80 	while (s_fgets(buf, sizeof (buf), fp) != NULL) {
     81 		if (strcmp(buf, "findroot") == 0) {
     82 			BAM_DPRINTF((D_FINDROOT_PRESENT, fcn));
     83 			bam_is_findroot = BAM_FINDROOT_PRESENT;
     84 		}
     85 		if (strcmp(buf, "dboot") == 0) {
     86 			BAM_DPRINTF((D_DBOOT_PRESENT, fcn));
     87 			dboot = 1;
     88 		}
     89 	}
     90 
     91 	assert(dboot);
     92 
     93 	if (bam_is_findroot == BAM_FINDROOT_UNKNOWN) {
     94 		bam_is_findroot = BAM_FINDROOT_ABSENT;
     95 		BAM_DPRINTF((D_FINDROOT_ABSENT, fcn));
     96 	}
     97 out:
     98 	ret = fclose(fp);
     99 	error = errno;
    100 	INJECT_ERROR1("GET_CAP_FINDROOT_FCLOSE", ret = 1);
    101 	if (ret != 0) {
    102 		bam_error(CLOSE_FAIL, path, strerror(error));
    103 	}
    104 }
    105 
    106 error_t
    107 get_boot_cap(const char *osroot)
    108 {
    109 	char		fname[PATH_MAX];
    110 	char		*image;
    111 	uchar_t		*ident;
    112 	int		fd;
    113 	int		m;
    114 	multiboot_header_t *mbh;
    115 	struct stat	sb;
    116 	int		error;
    117 	const char	*fcn = "get_boot_cap()";
    118 
    119 	if (is_sparc()) {
    120 		/* there is no non dboot sparc new-boot */
    121 		bam_direct = BAM_DIRECT_DBOOT;
    122 		BAM_DPRINTF((D_IS_SPARC_DBOOT, fcn));
    123 		return (BAM_SUCCESS);
    124 	}
    125 
    126 	if (!is_grub(osroot)) {
    127 		bam_error(NOT_GRUB_ROOT, osroot);
    128 		return (BAM_ERROR);
    129 	}
    130 
    131 	(void) snprintf(fname, PATH_MAX, "%s/%s", osroot,
    132 	    "platform/i86pc/kernel/unix");
    133 	fd = open(fname, O_RDONLY);
    134 	error = errno;
    135 	INJECT_ERROR1("GET_CAP_UNIX_OPEN", fd = -1);
    136 	if (fd < 0) {
    137 		bam_error(OPEN_FAIL, fname, strerror(error));
    138 		return (BAM_ERROR);
    139 	}
    140 
    141 	/*
    142 	 * Verify that this is a sane unix at least 8192 bytes in length
    143 	 */
    144 	if (fstat(fd, &sb) == -1 || sb.st_size < 8192) {
    145 		(void) close(fd);
    146 		bam_error(INVALID_BINARY, fname);
    147 		return (BAM_ERROR);
    148 	}
    149 
    150 	/*
    151 	 * mmap the first 8K
    152 	 */
    153 	image = mmap(NULL, 8192, PROT_READ, MAP_SHARED, fd, 0);
    154 	error = errno;
    155 	INJECT_ERROR1("GET_CAP_MMAP", image = MAP_FAILED);
    156 	if (image == MAP_FAILED) {
    157 		bam_error(MMAP_FAIL, fname, strerror(error));
    158 		return (BAM_ERROR);
    159 	}
    160 
    161 	ident = (uchar_t *)image;
    162 	if (ident[EI_MAG0] != ELFMAG0 || ident[EI_MAG1] != ELFMAG1 ||
    163 	    ident[EI_MAG2] != ELFMAG2 || ident[EI_MAG3] != ELFMAG3) {
    164 		bam_error(NOT_ELF_FILE, fname);
    165 		return (BAM_ERROR);
    166 	}
    167 	if (ident[EI_CLASS] != ELFCLASS32) {
    168 		bam_error(WRONG_ELF_CLASS, fname, ident[EI_CLASS]);
    169 		return (BAM_ERROR);
    170 	}
    171 
    172 	/*
    173 	 * The GRUB multiboot header must be 32-bit aligned and completely
    174 	 * contained in the 1st 8K of the file.  If the unix binary has
    175 	 * a multiboot header, then it is a 'dboot' kernel.  Otherwise,
    176 	 * this kernel must be booted via multiboot -- we call this a
    177 	 * 'multiboot' kernel.
    178 	 */
    179 	bam_direct = BAM_DIRECT_MULTIBOOT;
    180 	for (m = 0; m < 8192 - sizeof (multiboot_header_t); m += 4) {
    181 		mbh = (void *)(image + m);
    182 		if (mbh->magic == MB_HEADER_MAGIC) {
    183 			BAM_DPRINTF((D_IS_DBOOT, fcn));
    184 			bam_direct = BAM_DIRECT_DBOOT;
    185 			break;
    186 		}
    187 	}
    188 	(void) munmap(image, 8192);
    189 	(void) close(fd);
    190 
    191 	INJECT_ERROR1("GET_CAP_MULTIBOOT", bam_direct = BAM_DIRECT_MULTIBOOT);
    192 	if (bam_direct == BAM_DIRECT_DBOOT) {
    193 		if (bam_is_hv == BAM_HV_PRESENT) {
    194 			BAM_DPRINTF((D_IS_XVM, fcn));
    195 		} else {
    196 			BAM_DPRINTF((D_IS_NOT_XVM, fcn));
    197 		}
    198 	} else {
    199 		BAM_DPRINTF((D_IS_MULTIBOOT, fcn));
    200 	}
    201 
    202 	/* Not a fatal error if this fails */
    203 	get_findroot_cap(osroot);
    204 
    205 	BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
    206 	return (BAM_SUCCESS);
    207 }
    208 
    209 #define	INST_RELEASE	"var/sadm/system/admin/INST_RELEASE"
    210 
    211 /*
    212  * Return true if root has been bfu'ed.  bfu will blow away
    213  * var/sadm/system/admin/INST_RELEASE, so if it's still there, we can
    214  * assume the system has not been bfu'ed.
    215  */
    216 static int
    217 is_bfu_system(const char *root)
    218 {
    219 	static int		is_bfu = -1;
    220 	char			path[PATH_MAX];
    221 	struct stat		sb;
    222 	const char		*fcn = "is_bfu_system()";
    223 
    224 	if (is_bfu != -1) {
    225 		BAM_DPRINTF((D_ALREADY_BFU_TEST, fcn, is_bfu ? "" : "NOT"));
    226 		return (is_bfu);
    227 	}
    228 
    229 	(void) snprintf(path, sizeof (path), "%s/%s", root, INST_RELEASE);
    230 	if (stat(path, &sb) != 0) {
    231 		is_bfu = 1;
    232 		BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
    233 	} else {
    234 		is_bfu = 0;
    235 		BAM_DPRINTF((D_RETURN_FAILURE, fcn));
    236 	}
    237 	return (is_bfu);
    238 }
    239 
    240 #define	MENU_URL(root)	(is_bfu_system(root) ?		\
    241 	"http://www.sun.com/msg/SUNOS-8000-CF" :	\
    242 	"http://www.sun.com/msg/SUNOS-8000-AK")
    243 
    244 /*
    245  * Simply allocate a new line and copy in cmd + sep + arg
    246  */
    247 void
    248 update_line(line_t *linep)
    249 {
    250 	size_t		size;
    251 	const char	*fcn = "update_line()";
    252 
    253 	BAM_DPRINTF((D_UPDATE_LINE_BEFORE, fcn, linep->line));
    254 	free(linep->line);
    255 	size = strlen(linep->cmd) + strlen(linep->sep) + strlen(linep->arg) + 1;
    256 	linep->line = s_calloc(1, size);
    257 	(void) snprintf(linep->line, size, "%s%s%s", linep->cmd, linep->sep,
    258 	    linep->arg);
    259 	BAM_DPRINTF((D_UPDATE_LINE_AFTER, fcn, linep->line));
    260 }
    261 
    262 static char *
    263 skip_wspace(char *ptr)
    264 {
    265 	const char		*fcn = "skip_wspace()";
    266 
    267 	INJECT_ERROR1("SKIP_WSPACE", ptr = NULL);
    268 	if (ptr == NULL) {
    269 		BAM_DPRINTF((D_SKIP_WSPACE_PTR_NULL, fcn));
    270 		return (NULL);
    271 	}
    272 
    273 	BAM_DPRINTF((D_SKIP_WSPACE_ENTRY_PTR, fcn, ptr));
    274 	for (; *ptr != '\0'; ptr++) {
    275 		if ((*ptr != ' ') && (*ptr != '\t') &&
    276 		    (*ptr != '\n'))
    277 			break;
    278 	}
    279 
    280 	ptr = (*ptr == '\0' ? NULL : ptr);
    281 
    282 	BAM_DPRINTF((D_SKIP_WSPACE_EXIT_PTR, fcn, ptr ? ptr : "NULL"));
    283 
    284 	return (ptr);
    285 }
    286 
    287 static char *
    288 rskip_bspace(char *bound, char *ptr)
    289 {
    290 	const char		*fcn = "rskip_bspace()";
    291 	assert(bound);
    292 	assert(ptr);
    293 	assert(bound <= ptr);
    294 	assert(*bound != ' ' && *bound != '\t' && *bound != '\n');
    295 
    296 	BAM_DPRINTF((D_RSKIP_BSPACE_ENTRY, fcn, ptr));
    297 	for (; ptr > bound; ptr--) {
    298 		if (*ptr == ' ' || *ptr == '\t' || *ptr == '\n')
    299 			break;
    300 	}
    301 
    302 	BAM_DPRINTF((D_RSKIP_BSPACE_EXIT, fcn, ptr));
    303 	return (ptr);
    304 }
    305 
    306 /*
    307  * The parse_kernel_line function examines a menu.lst kernel line.  For
    308  * multiboot, this is:
    309  *
    310  * kernel <multiboot path> <flags1> <kernel path> <flags2>
    311  *
    312  * <multiboot path> is either /platform/i86pc/multiboot or /boot/multiboot
    313  *
    314  * <kernel path> may be missing, or may be any full or relative path to unix.
    315  *	We check for it by looking for a word ending in "/unix".  If it ends
    316  *	in "kernel/unix", we upgrade it to a 32-bit entry.  If it ends in
    317  *	"kernel/amd64/unix", we upgrade it to the default entry.  Otherwise,
    318  *	it's a custom kernel, and we skip it.
    319  *
    320  * <flags*> are anything that doesn't fit either of the above - these will be
    321  *	copied over.
    322  *
    323  * For direct boot, the defaults are
    324  *
    325  * kernel$ <kernel path> <flags>
    326  *
    327  * <kernel path> is one of:
    328  *	/platform/i86pc/kernel/$ISADIR/unix
    329  *	/boot/platform/i86pc/kernel/$ISADIR/unix
    330  *	/platform/i86pc/kernel/unix
    331  *	/platform/i86pc/kernel/amd64/unix
    332  *	/boot/platform/i86pc/kernel/unix
    333  *	/boot/platform/i86pc/kernel/amd64/unix
    334  *
    335  * If <kernel path> is any of the last four, the command may also be "kernel".
    336  *
    337  * <flags> is anything that isn't <kernel path>.
    338  *
    339  * This function is only called to convert a multiboot entry to a dboot entry
    340  *
    341  * For safety, we do one more check: if the kernel path starts with /boot,
    342  * we verify that the new kernel exists before changing it.  This is mainly
    343  * done for bfu, as it may cause the failsafe archives to be a different
    344  * boot architecture from the newly bfu'ed system.
    345  */
    346 static error_t
    347 cvt_kernel_line(line_t *line, const char *osroot, entry_t *entry)
    348 {
    349 	char		path[PATH_MAX], path_64[PATH_MAX];
    350 	char		linebuf[PATH_MAX];
    351 	char		new_arg[PATH_MAX];
    352 	struct stat	sb, sb_64;
    353 	char		*old_ptr;
    354 	char		*unix_ptr;
    355 	char		*flags1_ptr;
    356 	char		*flags2_ptr;
    357 	const char	*fcn = "cvt_kernel_line()";
    358 
    359 	BAM_DPRINTF((D_FUNC_ENTRY2, fcn, line->line, osroot));
    360 
    361 	/*
    362 	 * We only convert multiboot to dboot and nothing else.
    363 	 */
    364 	if (!(entry->flags & BAM_ENTRY_MULTIBOOT)) {
    365 		BAM_DPRINTF((D_NOT_MULTIBOOT_CONVERT, fcn));
    366 		return (BAM_SUCCESS);
    367 	}
    368 
    369 	if (entry->flags & BAM_ENTRY_FAILSAFE) {
    370 		/*
    371 		 * We're attempting to change failsafe to dboot.
    372 		 * In the bfu case, we may not have a dboot failsafe
    373 		 * kernel i.e. a "unix" under the "/boot" hierarchy.
    374 		 * If so, just emit a message in verbose mode and
    375 		 * return success.
    376 		 */
    377 		BAM_DPRINTF((D_TRYING_FAILSAFE_CVT_TO_DBOOT, fcn));
    378 		(void) snprintf(path, PATH_MAX, "%s%s", osroot,
    379 		    DIRECT_BOOT_FAILSAFE_32);
    380 		(void) snprintf(path_64, PATH_MAX, "%s%s", osroot,
    381 		    DIRECT_BOOT_FAILSAFE_64);
    382 		if (stat(path, &sb) != 0 && stat(path_64, &sb_64) != 0) {
    383 			if (bam_verbose) {
    384 				bam_error(FAILSAFE_MISSING, line->lineNum);
    385 			}
    386 			BAM_DPRINTF((D_NO_FAILSAFE_UNIX_CONVERT, fcn));
    387 			return (BAM_SUCCESS);
    388 		}
    389 	}
    390 
    391 	/*
    392 	 * Make sure we have the correct cmd
    393 	 */
    394 
    395 	free(line->cmd);
    396 	line->cmd = s_strdup(menu_cmds[KERNEL_DOLLAR_CMD]);
    397 	BAM_DPRINTF((D_CVT_CMD_KERN_DOLLAR, fcn, line->cmd));
    398 
    399 	assert(sizeof (linebuf) > strlen(line->arg) + 32);
    400 	(void) strlcpy(linebuf, line->arg, sizeof (linebuf));
    401 
    402 	old_ptr = strpbrk(linebuf, " \t\n");
    403 	old_ptr = skip_wspace(old_ptr);
    404 	if (old_ptr == NULL) {
    405 		/*
    406 		 * only multiboot and nothing else
    407 		 * i.e. flags1 = unix = flags2 = NULL
    408 		 */
    409 		flags1_ptr = unix_ptr = flags2_ptr = NULL;
    410 		BAM_DPRINTF((D_FLAGS1_UNIX_FLAGS2_NULL, fcn))
    411 		goto create;
    412 	}
    413 
    414 	/*
    415 	 *
    416 	 * old_ptr is either at "flags1" or "unix"
    417 	 */
    418 	if (unix_ptr = strstr(old_ptr, "/unix")) {
    419 
    420 		/*
    421 		 * There is a  unix.
    422 		 */
    423 		BAM_DPRINTF((D_UNIX_PRESENT, fcn));
    424 
    425 		/* See if there's a flags2 past unix */
    426 		flags2_ptr = unix_ptr + strlen("/unix");
    427 		flags2_ptr = skip_wspace(flags2_ptr);
    428 		if (flags2_ptr) {
    429 			BAM_DPRINTF((D_FLAGS2_PRESENT, fcn, flags2_ptr));
    430 		} else {
    431 			BAM_DPRINTF((D_FLAGS2_ABSENT, fcn));
    432 		}
    433 
    434 		/* see if there is a flags1 before unix */
    435 		unix_ptr = rskip_bspace(old_ptr, unix_ptr);
    436 
    437 		if (unix_ptr == old_ptr) {
    438 			flags1_ptr = NULL;
    439 			BAM_DPRINTF((D_FLAGS1_ABSENT, fcn));
    440 		} else {
    441 			flags1_ptr = old_ptr;
    442 			*unix_ptr = '\0';
    443 			unix_ptr++;
    444 			BAM_DPRINTF((D_FLAGS1_PRESENT, fcn, flags1_ptr));
    445 		}
    446 
    447 	} else  {
    448 		/* There is no unix, there is only a bunch of flags */
    449 		flags1_ptr = old_ptr;
    450 		unix_ptr = flags2_ptr = NULL;
    451 		BAM_DPRINTF((D_FLAGS1_ONLY, fcn, flags1_ptr));
    452 	}
    453 
    454 	/*
    455 	 * With dboot, unix is fixed and is at the beginning. We need to
    456 	 * migrate flags1 and flags2
    457 	 */
    458 create:
    459 	if (entry->flags & BAM_ENTRY_FAILSAFE) {
    460 		(void) snprintf(new_arg, sizeof (new_arg), "%s",
    461 		    DIRECT_BOOT_FAILSAFE_KERNEL);
    462 	} else {
    463 		(void) snprintf(new_arg, sizeof (new_arg), "%s",
    464 		    DIRECT_BOOT_KERNEL);
    465 	}
    466 	BAM_DPRINTF((D_CVTED_UNIX, fcn, new_arg));
    467 
    468 	if (flags1_ptr != NULL) {
    469 		(void) strlcat(new_arg, " ", sizeof (new_arg));
    470 		(void) strlcat(new_arg, flags1_ptr, sizeof (new_arg));
    471 	}
    472 
    473 	if (flags2_ptr != NULL) {
    474 		(void) strlcat(new_arg, " ", sizeof (new_arg));
    475 		(void) strlcat(new_arg, flags2_ptr, sizeof (new_arg));
    476 	}
    477 
    478 	BAM_DPRINTF((D_CVTED_UNIX_AND_FLAGS, fcn, new_arg));
    479 
    480 	free(line->arg);
    481 	line->arg = s_strdup(new_arg);
    482 	update_line(line);
    483 	BAM_DPRINTF((D_CVTED_KERNEL_LINE, fcn, line->line));
    484 	return (BAM_SUCCESS);
    485 }
    486 
    487 /*
    488  * Similar to above, except this time we're looking at a module line,
    489  * which is quite a bit simpler.
    490  *
    491  * Under multiboot, the archive line is:
    492  *
    493  * module /platform/i86pc/boot_archive
    494  *
    495  * Under directboot, the archive line is:
    496  *
    497  * module$ /platform/i86pc/$ISADIR/boot_archive
    498  *
    499  * which may be specified exactly as either of:
    500  *
    501  * module /platform/i86pc/boot_archive
    502  * module /platform/i86pc/amd64/boot_archive
    503  *
    504  * Under multiboot, the failsafe is:
    505  *
    506  * module /boot/x86.miniroot-safe
    507  *
    508  * Under dboot, the failsafe is:
    509  *
    510  * module$ /boot/$ISADIR/x86.miniroot-safe
    511  *
    512  * which may be specified exactly as either of:
    513  *
    514  * module /boot/x86.miniroot-safe
    515  * module /boot/amd64/x86.miniroot-safe
    516  */
    517 static error_t
    518 cvt_module_line(line_t *line, entry_t *entry)
    519 {
    520 	const char		*fcn = "cvt_module_line()";
    521 
    522 	BAM_DPRINTF((D_FUNC_ENTRY1, fcn, line->line));
    523 
    524 	/*
    525 	 * We only convert multiboot to dboot and nothing else
    526 	 */
    527 	if (!(entry->flags & BAM_ENTRY_MULTIBOOT)) {
    528 		BAM_DPRINTF((D_NOT_MULTIBOOT_CONVERT, fcn));
    529 		return (BAM_SUCCESS);
    530 	}
    531 
    532 	if (entry->flags & BAM_ENTRY_FAILSAFE) {
    533 		if (strcmp(line->arg, FAILSAFE_ARCHIVE) == 0) {
    534 			BAM_DPRINTF((D_FAILSAFE_NO_CVT_NEEDED, fcn, line->arg));
    535 			BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
    536 			return (BAM_SUCCESS);
    537 		}
    538 	} else if (strcmp(line->arg, MULTIBOOT_ARCHIVE) != 0) {
    539 		bam_error(UNKNOWN_MODULE_LINE, line->lineNum);
    540 		BAM_DPRINTF((D_RETURN_FAILURE, fcn));
    541 		return (BAM_MSG);
    542 	}
    543 
    544 	free(line->cmd);
    545 	free(line->arg);
    546 	line->cmd = s_strdup(menu_cmds[MODULE_DOLLAR_CMD]);
    547 
    548 	line->arg = s_strdup(entry->flags & BAM_ENTRY_FAILSAFE ?
    549 	    FAILSAFE_ARCHIVE : DIRECT_BOOT_ARCHIVE);
    550 
    551 	update_line(line);
    552 	BAM_DPRINTF((D_CVTED_MODULE, fcn, line->line));
    553 	BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
    554 	return (BAM_SUCCESS);
    555 }
    556 
    557 static void
    558 bam_warn_hand_entries(menu_t *mp, char *osroot)
    559 {
    560 	int		hand_num;
    561 	int		hand_max;
    562 	int		*hand_list;
    563 	int		i;
    564 	entry_t		*entry;
    565 	const char	*fcn = "bam_warn_hand_entries()";
    566 
    567 	if (bam_force) {
    568 		/*
    569 		 * No warning needed, we are automatically converting
    570 		 * the "hand" entries
    571 		 */
    572 		BAM_DPRINTF((D_FORCE_HAND_CVT,  fcn));
    573 		return;
    574 	}
    575 
    576 	hand_num = 0;
    577 	hand_max = BAM_ENTRY_NUM;
    578 	hand_list = s_calloc(1, hand_max);
    579 
    580 	for (entry = mp->entries; entry; entry = entry->next) {
    581 		if (entry->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU))
    582 			continue;
    583 		BAM_DPRINTF((D_FOUND_HAND, fcn, entry->entryNum));
    584 		if (++hand_num > hand_max) {
    585 			hand_max *= 2;
    586 			hand_list = s_realloc(hand_list,
    587 			    hand_max * sizeof (int));
    588 		}
    589 		hand_list[hand_num - 1] = entry->entryNum;
    590 	}
    591 
    592 	bam_error(HAND_ADDED_ENTRIES, osroot, MENU_URL(osroot));
    593 	bam_print_stderr("Entry Number%s: ", (hand_num > 1) ?
    594 	    "s" : "");
    595 	for (i = 0; i < hand_num; i++) {
    596 		bam_print_stderr("%d ", hand_list[i]);
    597 	}
    598 	bam_print_stderr("\n");
    599 }
    600 
    601 static entry_t *
    602 find_matching_entry(
    603 	entry_t *estart,
    604 	char *grubsign,
    605 	char *grubroot,
    606 	int root_opt)
    607 {
    608 	entry_t		*entry;
    609 	line_t		*line;
    610 	char		opt[10];
    611 	const char	*fcn = "find_matching_entry()";
    612 
    613 	assert(grubsign);
    614 	assert(root_opt == 0 || root_opt == 1);
    615 
    616 	(void) snprintf(opt, sizeof (opt), "%d", root_opt);
    617 	BAM_DPRINTF((D_FUNC_ENTRY3, fcn, grubsign, grubroot, opt));
    618 
    619 	for (entry = estart; entry; entry = entry->next) {
    620 
    621 		if (!(entry->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU)) &&
    622 		    !bam_force) {
    623 			BAM_DPRINTF((D_SKIP_ENTRY, fcn, entry->entryNum));
    624 			continue;
    625 		}
    626 
    627 		if (entry->flags & BAM_ENTRY_ROOT) {
    628 			for (line = entry->start; line; line = line->next) {
    629 				if (line->cmd == NULL || line->arg == NULL) {
    630 					if (line == entry->end) {
    631 						BAM_DPRINTF((D_ENTRY_END, fcn));
    632 						break;
    633 					} else {
    634 						BAM_DPRINTF((D_SKIP_NULL, fcn));
    635 						continue;
    636 					}
    637 				}
    638 				if (strcmp(line->cmd, menu_cmds[ROOT_CMD])
    639 				    == 0 && strcmp(line->arg, grubroot) == 0) {
    640 					BAM_DPRINTF((D_ROOT_MATCH, fcn,
    641 					    line->line, grubsign));
    642 					return (entry);
    643 				}
    644 				if (line == entry->end) {
    645 					BAM_DPRINTF((D_ENTRY_END, fcn));
    646 					break;
    647 				}
    648 			}
    649 		} else if (entry->flags & BAM_ENTRY_FINDROOT) {
    650 			for (line = entry->start; line; line = line->next) {
    651 				if (line->cmd == NULL || line->arg == NULL) {
    652 					if (line == entry->end) {
    653 						BAM_DPRINTF((D_ENTRY_END, fcn));
    654 						break;
    655 					} else {
    656 						BAM_DPRINTF((D_SKIP_NULL, fcn));
    657 						continue;
    658 					}
    659 				}
    660 				if (strcmp(line->cmd, menu_cmds[FINDROOT_CMD])
    661 				    == 0 && strcmp(line->arg, grubsign) == 0) {
    662 					BAM_DPRINTF((D_FINDROOT_MATCH, fcn,
    663 					    line->line, grubsign));
    664 					return (entry);
    665 				}
    666 				if (line == entry->end) {
    667 					BAM_DPRINTF((D_ENTRY_END, fcn));
    668 					break;
    669 				}
    670 			}
    671 		} else if (root_opt) {
    672 			/* Neither root nor findroot */
    673 			BAM_DPRINTF((D_NO_ROOT_FINDROOT, fcn, entry->entryNum));
    674 			return (entry);
    675 		}
    676 	}
    677 
    678 	BAM_DPRINTF((D_NO_MATCH, fcn));
    679 	return (NULL);
    680 }
    681 
    682 /*
    683  * The following is a set of routines that attempt to convert the
    684  * menu entries for the supplied osroot into a format compatible
    685  * with the GRUB installation on osroot.
    686  *
    687  * Each of these conversion routines make no assumptions about
    688  * the current state of the menu entry, it does its best to
    689  * convert the menu entry to the new state. In the process
    690  * we may either upgrade or downgrade.
    691  *
    692  * We don't make any heroic efforts at conversion. It is better
    693  * to be conservative and bail out at the first sign of error. We will
    694  * in such cases, point the user at the knowledge-base article
    695  * so that they can upgrade manually.
    696  */
    697 static error_t
    698 bam_add_findroot(menu_t *mp, char *grubsign, char *grubroot, int root_opt)
    699 {
    700 	entry_t		*entry;
    701 	line_t		*line;
    702 	line_t		*newlp;
    703 	int		update_num;
    704 	char		linebuf[PATH_MAX];
    705 	const char	*fcn = "bam_add_findroot()";
    706 
    707 	update_num = 0;
    708 
    709 	bam_print(CVT_FINDROOT);
    710 
    711 	entry = mp->entries;
    712 	for (; entry = find_matching_entry(entry, grubsign, grubroot, root_opt);
    713 	    entry = entry->next) {
    714 		if (entry->flags & BAM_ENTRY_FINDROOT) {
    715 			/* already converted */
    716 			BAM_DPRINTF((D_ALREADY_FINDROOT, fcn, entry->entryNum));
    717 			continue;
    718 		}
    719 		for (line = entry->start; line; line = line->next) {
    720 			if (line->cmd == NULL || line->arg == NULL) {
    721 				if (line == entry->end) {
    722 					BAM_DPRINTF((D_ENTRY_END, fcn));
    723 					break;
    724 				} else {
    725 					BAM_DPRINTF((D_SKIP_NULL, fcn));
    726 					continue;
    727 				}
    728 			}
    729 			if (strcmp(line->cmd, menu_cmds[TITLE_CMD]) == 0) {
    730 				newlp = s_calloc(1, sizeof (line_t));
    731 				newlp->cmd = s_strdup(menu_cmds[FINDROOT_CMD]);
    732 				newlp->sep = s_strdup(" ");
    733 				newlp->arg = s_strdup(grubsign);
    734 				(void) snprintf(linebuf, sizeof (linebuf),
    735 				    "%s%s%s", newlp->cmd, newlp->sep,
    736 				    newlp->arg);
    737 				newlp->line = s_strdup(linebuf);
    738 				bam_add_line(mp, entry, line, newlp);
    739 				update_num = 1;
    740 				entry->flags &= ~BAM_ENTRY_ROOT;
    741 				entry->flags |= BAM_ENTRY_FINDROOT;
    742 				BAM_DPRINTF((D_ADDED_FINDROOT, fcn,
    743 				    newlp->line));
    744 				line = newlp;
    745 			}
    746 			if (strcmp(line->cmd, menu_cmds[ROOT_CMD]) == 0) {
    747 				BAM_DPRINTF((D_FREEING_ROOT, fcn, line->line));
    748 				unlink_line(mp, line);
    749 				line_free(line);
    750 			}
    751 			if (line == entry->end) {
    752 				BAM_DPRINTF((D_ENTRY_END, fcn));
    753 				break;
    754 			}
    755 		}
    756 	}
    757 
    758 	if (update_num) {
    759 		BAM_DPRINTF((D_UPDATED_NUMBERING, fcn));
    760 		update_numbering(mp);
    761 	}
    762 
    763 	BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
    764 	return (BAM_SUCCESS);
    765 }
    766 
    767 static error_t
    768 bam_add_hv(menu_t *mp, char *grubsign, char *grubroot, int root_opt)
    769 {
    770 	entry_t		*entry;
    771 	const char	*fcn = "bam_add_hv()";
    772 
    773 	bam_print(CVT_HV);
    774 
    775 	entry = mp->entries;
    776 	for (; entry = find_matching_entry(entry, grubsign, grubroot, root_opt);
    777 	    entry = entry->next) {
    778 		if (entry->flags & BAM_ENTRY_HV) {
    779 			BAM_DPRINTF((D_ALREADY_HV, fcn, entry->entryNum));
    780 			return (BAM_SUCCESS);
    781 		}
    782 	}
    783 
    784 	(void) add_boot_entry(mp, NEW_HV_ENTRY, grubsign, XEN_MENU,
    785 	    XEN_KERNEL_MODULE_LINE, DIRECT_BOOT_ARCHIVE, NULL);
    786 
    787 	BAM_DPRINTF((D_ADDED_XVM_ENTRY, fcn));
    788 
    789 	update_numbering(mp);
    790 
    791 	BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
    792 
    793 	return (BAM_SUCCESS);
    794 }
    795 
    796 static error_t
    797 bam_add_dboot(
    798 	menu_t *mp,
    799 	char *osroot,
    800 	char *grubsign,
    801 	char *grubroot,
    802 	int root_opt)
    803 {
    804 	int		msg = 0;
    805 	entry_t		*entry;
    806 	line_t		*line;
    807 	error_t		ret;
    808 	const char 	*fcn = "bam_add_dboot()";
    809 
    810 	bam_print(CVT_DBOOT);
    811 
    812 	entry = mp->entries;
    813 	for (; entry = find_matching_entry(entry, grubsign, grubroot, root_opt);
    814 	    entry = entry->next) {
    815 		for (line = entry->start; line; line = line->next) {
    816 			if (line->cmd == NULL || line->arg == NULL) {
    817 				if (line == entry->end) {
    818 					BAM_DPRINTF((D_ENTRY_END, fcn));
    819 					break;
    820 				} else {
    821 					BAM_DPRINTF((D_SKIP_NULL, fcn));
    822 					continue;
    823 				}
    824 			}
    825 
    826 			/*
    827 			 * If we have a kernel$ command, assume it
    828 			 * is dboot already.  If it is not a dboot
    829 			 * entry, something funny is going on and
    830 			 * we will leave it alone
    831 			 */
    832 			if (strcmp(line->cmd, menu_cmds[KERNEL_CMD]) == 0) {
    833 				ret = cvt_kernel_line(line, osroot, entry);
    834 				INJECT_ERROR1("ADD_DBOOT_KERN_ERR",
    835 				    ret = BAM_ERROR);
    836 				INJECT_ERROR1("ADD_DBOOT_KERN_MSG",
    837 				    ret = BAM_MSG);
    838 				if (ret == BAM_ERROR) {
    839 					BAM_DPRINTF((D_CVT_KERNEL_FAIL, fcn));
    840 					return (ret);
    841 				} else if (ret == BAM_MSG) {
    842 					msg = 1;
    843 					BAM_DPRINTF((D_CVT_KERNEL_MSG, fcn));
    844 				}
    845 			}
    846 			if (strcmp(line->cmd, menu_cmds[MODULE_CMD]) == 0) {
    847 				ret = cvt_module_line(line, entry);
    848 				INJECT_ERROR1("ADD_DBOOT_MOD_ERR",
    849 				    ret = BAM_ERROR);
    850 				INJECT_ERROR1("ADD_DBOOT_MOD_MSG",
    851 				    ret = BAM_MSG);
    852 				if (ret == BAM_ERROR) {
    853 					BAM_DPRINTF((D_CVT_MODULE_FAIL, fcn));
    854 					return (ret);
    855 				} else if (ret == BAM_MSG) {
    856 					BAM_DPRINTF((D_CVT_MODULE_MSG, fcn));
    857 					msg = 1;
    858 				}
    859 			}
    860 
    861 			if (line == entry->end) {
    862 				BAM_DPRINTF((D_ENTRY_END, fcn));
    863 				break;
    864 			}
    865 		}
    866 	}
    867 
    868 	ret = msg ? BAM_MSG : BAM_SUCCESS;
    869 	BAM_DPRINTF((D_RETURN_RET, fcn, ret));
    870 	return (ret);
    871 }
    872 
    873 /*ARGSUSED*/
    874 error_t
    875 upgrade_menu(menu_t *mp, char *osroot, char *menu_root)
    876 {
    877 	char		*osdev;
    878 	char		*grubsign;
    879 	char		*grubroot;
    880 	int		ret1;
    881 	int		ret2;
    882 	int		ret3;
    883 	const char	*fcn = "upgrade_menu()";
    884 
    885 	assert(osroot);
    886 	assert(menu_root);
    887 
    888 	BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, menu_root));
    889 
    890 	/*
    891 	 * We only support upgrades. Xen may not be present
    892 	 * on smaller metaclusters so we don't check for that.
    893 	 */
    894 	if (bam_is_findroot != BAM_FINDROOT_PRESENT ||
    895 	    bam_direct != BAM_DIRECT_DBOOT) {
    896 		bam_error(DOWNGRADE_NOTSUP, osroot);
    897 		return (BAM_ERROR);
    898 	}
    899 
    900 	/*
    901 	 * First get the GRUB signature
    902 	 */
    903 	osdev = get_special(osroot);
    904 	INJECT_ERROR1("UPGRADE_OSDEV", osdev = NULL);
    905 	if (osdev == NULL) {
    906 		bam_error(CANT_FIND_SPECIAL, osroot);
    907 		return (BAM_ERROR);
    908 	}
    909 
    910 	grubsign = get_grubsign(osroot, osdev);
    911 	INJECT_ERROR1("UPGRADE_GRUBSIGN", grubsign = NULL);
    912 	if (grubsign == NULL) {
    913 		free(osdev);
    914 		bam_error(CANT_FIND_GRUBSIGN, osroot);
    915 		return (BAM_ERROR);
    916 	}
    917 
    918 	/* not fatal if we can't get grubroot */
    919 	grubroot = get_grubroot(osroot, osdev, menu_root);
    920 	INJECT_ERROR1("UPGRADE_GRUBROOT", grubroot = NULL);
    921 
    922 	free(osdev);
    923 
    924 	ret1 = bam_add_findroot(mp, grubsign,
    925 	    grubroot, root_optional(osroot, menu_root));
    926 	INJECT_ERROR1("UPGRADE_ADD_FINDROOT", ret1 = BAM_ERROR);
    927 	if (ret1 == BAM_ERROR)
    928 		goto abort;
    929 
    930 	if (bam_is_hv == BAM_HV_PRESENT) {
    931 		ret2 = bam_add_hv(mp, grubsign, grubroot,
    932 		    root_optional(osroot, menu_root));
    933 		INJECT_ERROR1("UPGRADE_ADD_HV", ret2 = BAM_ERROR);
    934 		if (ret2 == BAM_ERROR)
    935 			goto abort;
    936 	} else
    937 		ret2 = BAM_SUCCESS;
    938 
    939 	ret3 = bam_add_dboot(mp, osroot, grubsign,
    940 	    grubroot, root_optional(osroot, menu_root));
    941 	INJECT_ERROR1("UPGRADE_ADD_DBOOT", ret3 = BAM_ERROR);
    942 	if (ret3 == BAM_ERROR)
    943 		goto abort;
    944 
    945 	if (ret1 == BAM_MSG || ret2 == BAM_MSG || ret3 == BAM_MSG) {
    946 		bam_error(CVT_TODO, MENU_URL(osroot));
    947 	} else {
    948 		bam_warn_hand_entries(mp, osroot);
    949 	}
    950 
    951 	free(grubsign);
    952 
    953 	BAM_DPRINTF((D_RETURN_RET, fcn, BAM_WRITE));
    954 	return (BAM_WRITE);
    955 
    956 abort:
    957 	free(grubsign);
    958 	bam_error(CVT_ABORT, osroot, MENU_URL(osroot));
    959 	return (BAM_ERROR);
    960 }
    961