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