8142 bootadm: get_boot_cap() should test for both 32-bit and 64-bit kernel
[unleashed.git] / usr / src / cmd / boot / bootadm / bootadm_upgrade.c
blob86a184e62318869fea08c1ead43793432f9c891d
1 /*
2 * CDDL HEADER START
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]
19 * CDDL HEADER END
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 * Copyright 2017 Toomas Soome <tsoome@me.com>
27 #include <stdio.h>
28 #include <errno.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <limits.h>
35 #include <fcntl.h>
36 #include <strings.h>
38 #include <sys/mman.h>
39 #include <sys/elf.h>
40 #include <sys/multiboot.h>
42 #include "bootadm.h"
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;
48 static void
49 get_findroot_cap(const char *osroot)
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()";
60 (void) snprintf(path, sizeof (path), "%s/%s",
61 osroot, "boot/grub/capability");
63 if (stat(path, &sb) == -1) {
64 bam_is_findroot = BAM_FINDROOT_ABSENT;
65 BAM_DPRINTF(("%s: findroot capability absent\n", fcn));
66 return;
69 fp = fopen(path, "r");
70 error = errno;
71 INJECT_ERROR1("GET_CAP_FINDROOT_FOPEN", fp = NULL);
72 if (fp == NULL) {
73 bam_error(_("failed to open file: %s: %s\n"), path,
74 strerror(error));
75 return;
78 dboot = 0;
79 while (s_fgets(buf, sizeof (buf), fp) != NULL) {
80 if (strcmp(buf, "findroot") == 0) {
81 BAM_DPRINTF(("%s: findroot capability present\n", fcn));
82 bam_is_findroot = BAM_FINDROOT_PRESENT;
84 if (strcmp(buf, "dboot") == 0) {
85 BAM_DPRINTF(("%s: dboot capability present\n", fcn));
86 dboot = 1;
90 assert(dboot);
92 if (bam_is_findroot == BAM_FINDROOT_UNKNOWN) {
93 bam_is_findroot = BAM_FINDROOT_ABSENT;
94 BAM_DPRINTF(("%s: findroot capability absent\n", fcn));
97 ret = fclose(fp);
98 error = errno;
99 INJECT_ERROR1("GET_CAP_FINDROOT_FCLOSE", ret = 1);
100 if (ret != 0) {
101 bam_error(_("failed to close file: %s: %s\n"),
102 path, strerror(error));
106 error_t
107 get_boot_cap(const char *osroot)
109 char fname[PATH_MAX];
110 char *image;
111 uchar_t *ident;
112 uchar_t class;
113 int fd;
114 int m;
115 multiboot_header_t *mbh;
116 struct stat sb;
117 int error;
118 const char *fcn = "get_boot_cap()";
120 if (is_sparc()) {
121 /* there is no non dboot sparc new-boot */
122 bam_direct = BAM_DIRECT_DBOOT;
123 BAM_DPRINTF(("%s: is sparc - always DBOOT\n", fcn));
124 return (BAM_SUCCESS);
128 * The install media can support both 64 and 32 bit boot
129 * by using boot archive as ramdisk image. However, to save
130 * the memory, the ramdisk may only have either 32 or 64
131 * bit kernel files. To avoid error message about missing unix,
132 * we should try both variants here and only complain if neither
133 * is found. Since the 64-bit systems are more common, we start
134 * from amd64.
136 class = ELFCLASS64;
137 (void) snprintf(fname, PATH_MAX, "%s/%s", osroot,
138 "platform/i86pc/kernel/amd64/unix");
139 fd = open(fname, O_RDONLY);
140 if (fd < 0) {
141 class = ELFCLASS32;
142 (void) snprintf(fname, PATH_MAX, "%s/%s", osroot,
143 "platform/i86pc/kernel/unix");
144 fd = open(fname, O_RDONLY);
146 error = errno;
147 INJECT_ERROR1("GET_CAP_UNIX_OPEN", fd = -1);
148 if (fd < 0) {
149 bam_error(_("failed to open file: %s: %s\n"), fname,
150 strerror(error));
151 return (BAM_ERROR);
155 * Verify that this is a sane unix at least 8192 bytes in length
157 if (fstat(fd, &sb) == -1 || sb.st_size < 8192) {
158 (void) close(fd);
159 bam_error(_("invalid or corrupted binary: %s\n"), fname);
160 return (BAM_ERROR);
164 * mmap the first 8K
166 image = mmap(NULL, 8192, PROT_READ, MAP_SHARED, fd, 0);
167 error = errno;
168 INJECT_ERROR1("GET_CAP_MMAP", image = MAP_FAILED);
169 if (image == MAP_FAILED) {
170 bam_error(_("failed to mmap file: %s: %s\n"), fname,
171 strerror(error));
172 return (BAM_ERROR);
175 ident = (uchar_t *)image;
176 if (ident[EI_MAG0] != ELFMAG0 || ident[EI_MAG1] != ELFMAG1 ||
177 ident[EI_MAG2] != ELFMAG2 || ident[EI_MAG3] != ELFMAG3) {
178 bam_error(_("%s is not an ELF file.\n"), fname);
179 return (BAM_ERROR);
181 if (ident[EI_CLASS] != class) {
182 bam_error(_("%s is wrong ELF class 0x%x\n"), fname,
183 ident[EI_CLASS]);
184 return (BAM_ERROR);
188 * The GRUB multiboot header must be 32-bit aligned and completely
189 * contained in the 1st 8K of the file. If the unix binary has
190 * a multiboot header, then it is a 'dboot' kernel. Otherwise,
191 * this kernel must be booted via multiboot -- we call this a
192 * 'multiboot' kernel.
194 bam_direct = BAM_DIRECT_MULTIBOOT;
195 for (m = 0; m < 8192 - sizeof (multiboot_header_t); m += 4) {
196 mbh = (void *)(image + m);
197 if (mbh->magic == MB_HEADER_MAGIC) {
198 BAM_DPRINTF(("%s: is DBOOT unix\n", fcn));
199 bam_direct = BAM_DIRECT_DBOOT;
200 break;
203 (void) munmap(image, 8192);
204 (void) close(fd);
206 INJECT_ERROR1("GET_CAP_MULTIBOOT", bam_direct = BAM_DIRECT_MULTIBOOT);
207 if (bam_direct == BAM_DIRECT_DBOOT) {
208 if (bam_is_hv == BAM_HV_PRESENT) {
209 BAM_DPRINTF(("%s: is xVM system\n", fcn));
210 } else {
211 BAM_DPRINTF(("%s: is *NOT* xVM system\n", fcn));
213 } else {
214 BAM_DPRINTF(("%s: is MULTIBOOT unix\n", fcn));
217 /* Not a fatal error if this fails */
218 get_findroot_cap(osroot);
220 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
221 return (BAM_SUCCESS);
224 #define INST_RELEASE "var/sadm/system/admin/INST_RELEASE"
227 * Return true if root has been bfu'ed. bfu will blow away
228 * var/sadm/system/admin/INST_RELEASE, so if it's still there, we can
229 * assume the system has not been bfu'ed.
231 static int
232 is_bfu_system(const char *root)
234 static int is_bfu = -1;
235 char path[PATH_MAX];
236 struct stat sb;
237 const char *fcn = "is_bfu_system()";
239 if (is_bfu != -1) {
240 BAM_DPRINTF(("%s: already done bfu test. bfu is %s present\n",
241 fcn, is_bfu ? "" : "NOT"));
242 return (is_bfu);
245 (void) snprintf(path, sizeof (path), "%s/%s", root, INST_RELEASE);
246 if (stat(path, &sb) != 0) {
247 is_bfu = 1;
248 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
249 } else {
250 is_bfu = 0;
251 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
253 return (is_bfu);
256 #define MENU_URL(root) (is_bfu_system(root) ? \
257 "http://illumos.org/msg/SUNOS-8000-CF" : \
258 "http://illumos.org/msg/SUNOS-8000-AK")
261 * Simply allocate a new line and copy in cmd + sep + arg
263 void
264 update_line(line_t *linep)
266 size_t size;
267 const char *fcn = "update_line()";
269 BAM_DPRINTF(("%s: line before update: %s\n", fcn, linep->line));
270 free(linep->line);
271 size = strlen(linep->cmd) + strlen(linep->sep) + strlen(linep->arg) + 1;
272 linep->line = s_calloc(1, size);
273 (void) snprintf(linep->line, size, "%s%s%s", linep->cmd, linep->sep,
274 linep->arg);
275 BAM_DPRINTF(("%s: line after update: %s\n", fcn, linep->line));
278 static char *
279 skip_wspace(char *ptr)
281 const char *fcn = "skip_wspace()";
283 INJECT_ERROR1("SKIP_WSPACE", ptr = NULL);
284 if (ptr == NULL) {
285 BAM_DPRINTF(("%s: NULL ptr\n", fcn));
286 return (NULL);
289 BAM_DPRINTF(("%s: ptr on entry: %s\n", fcn, ptr));
290 for (; *ptr != '\0'; ptr++) {
291 if ((*ptr != ' ') && (*ptr != '\t') &&
292 (*ptr != '\n'))
293 break;
296 ptr = (*ptr == '\0' ? NULL : ptr);
298 BAM_DPRINTF(("%s: ptr on exit: %s\n", fcn, ptr ? ptr : "NULL"));
300 return (ptr);
303 static char *
304 rskip_bspace(char *bound, char *ptr)
306 const char *fcn = "rskip_bspace()";
307 assert(bound);
308 assert(ptr);
309 assert(bound <= ptr);
310 assert(*bound != ' ' && *bound != '\t' && *bound != '\n');
312 BAM_DPRINTF(("%s: ptr on entry: %s\n", fcn, ptr));
313 for (; ptr > bound; ptr--) {
314 if (*ptr == ' ' || *ptr == '\t' || *ptr == '\n')
315 break;
318 BAM_DPRINTF(("%s: ptr on exit: %s\n", fcn, ptr));
319 return (ptr);
323 * The parse_kernel_line function examines a menu.lst kernel line. For
324 * multiboot, this is:
326 * kernel <multiboot path> <flags1> <kernel path> <flags2>
328 * <multiboot path> is either /platform/i86pc/multiboot or /boot/multiboot
330 * <kernel path> may be missing, or may be any full or relative path to unix.
331 * We check for it by looking for a word ending in "/unix". If it ends
332 * in "kernel/unix", we upgrade it to a 32-bit entry. If it ends in
333 * "kernel/amd64/unix", we upgrade it to the default entry. Otherwise,
334 * it's a custom kernel, and we skip it.
336 * <flags*> are anything that doesn't fit either of the above - these will be
337 * copied over.
339 * For direct boot, the defaults are
341 * kernel$ <kernel path> <flags>
343 * <kernel path> is one of:
344 * /platform/i86pc/kernel/$ISADIR/unix
345 * /boot/platform/i86pc/kernel/$ISADIR/unix
346 * /platform/i86pc/kernel/unix
347 * /platform/i86pc/kernel/amd64/unix
348 * /boot/platform/i86pc/kernel/unix
349 * /boot/platform/i86pc/kernel/amd64/unix
351 * If <kernel path> is any of the last four, the command may also be "kernel".
353 * <flags> is anything that isn't <kernel path>.
355 * This function is only called to convert a multiboot entry to a dboot entry
357 * For safety, we do one more check: if the kernel path starts with /boot,
358 * we verify that the new kernel exists before changing it. This is mainly
359 * done for bfu, as it may cause the failsafe archives to be a different
360 * boot architecture from the newly bfu'ed system.
362 static error_t
363 cvt_kernel_line(line_t *line, const char *osroot, entry_t *entry)
365 char path[PATH_MAX], path_64[PATH_MAX];
366 char linebuf[PATH_MAX];
367 char new_arg[PATH_MAX];
368 struct stat sb, sb_64;
369 char *old_ptr;
370 char *unix_ptr;
371 char *flags1_ptr;
372 char *flags2_ptr;
373 const char *fcn = "cvt_kernel_line()";
375 BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, line->line, osroot));
378 * We only convert multiboot to dboot and nothing else.
380 if (!(entry->flags & BAM_ENTRY_MULTIBOOT)) {
381 BAM_DPRINTF(("%s: not MULTIBOOT, not converting\n", fcn));
382 return (BAM_SUCCESS);
385 if (entry->flags & BAM_ENTRY_FAILSAFE) {
387 * We're attempting to change failsafe to dboot.
388 * In the bfu case, we may not have a dboot failsafe
389 * kernel i.e. a "unix" under the "/boot" hierarchy.
390 * If so, just emit a message in verbose mode and
391 * return success.
393 BAM_DPRINTF(("%s: trying to convert failsafe to DBOOT\n", fcn));
394 (void) snprintf(path, PATH_MAX, "%s%s", osroot,
395 DIRECT_BOOT_FAILSAFE_32);
396 (void) snprintf(path_64, PATH_MAX, "%s%s", osroot,
397 DIRECT_BOOT_FAILSAFE_64);
398 if (stat(path, &sb) != 0 && stat(path_64, &sb_64) != 0) {
399 if (bam_verbose) {
400 bam_error(_("bootadm -m upgrade run, but the "
401 "failsafe archives have not been\nupdated. "
402 "Not updating line %d\n"), line->lineNum);
404 BAM_DPRINTF(("%s: no FAILSAFE unix, not converting\n",
405 fcn));
406 return (BAM_SUCCESS);
411 * Make sure we have the correct cmd
414 free(line->cmd);
415 line->cmd = s_strdup(menu_cmds[KERNEL_DOLLAR_CMD]);
416 BAM_DPRINTF(("%s: converted kernel cmd to %s\n", fcn, line->cmd));
418 assert(sizeof (linebuf) > strlen(line->arg) + 32);
419 (void) strlcpy(linebuf, line->arg, sizeof (linebuf));
421 old_ptr = strpbrk(linebuf, " \t\n");
422 old_ptr = skip_wspace(old_ptr);
423 if (old_ptr == NULL) {
425 * only multiboot and nothing else
426 * i.e. flags1 = unix = flags2 = NULL
428 flags1_ptr = unix_ptr = flags2_ptr = NULL;
429 BAM_DPRINTF(("%s: NULL flags1, unix, flags2\n", fcn))
430 goto create;
435 * old_ptr is either at "flags1" or "unix"
437 if ((unix_ptr = strstr(old_ptr, "/unix")) != NULL) {
440 * There is a unix.
442 BAM_DPRINTF(("%s: unix present\n", fcn));
444 /* See if there's a flags2 past unix */
445 flags2_ptr = unix_ptr + strlen("/unix");
446 flags2_ptr = skip_wspace(flags2_ptr);
447 if (flags2_ptr) {
448 BAM_DPRINTF(("%s: flags2 present: %s\n", fcn,
449 flags2_ptr));
450 } else {
451 BAM_DPRINTF(("%s: flags2 absent\n", fcn));
454 /* see if there is a flags1 before unix */
455 unix_ptr = rskip_bspace(old_ptr, unix_ptr);
457 if (unix_ptr == old_ptr) {
458 flags1_ptr = NULL;
459 BAM_DPRINTF(("%s: flags1 absent\n", fcn));
460 } else {
461 flags1_ptr = old_ptr;
462 *unix_ptr = '\0';
463 unix_ptr++;
464 BAM_DPRINTF(("%s: flags1 present: %s\n", fcn,
465 flags1_ptr));
468 } else {
469 /* There is no unix, there is only a bunch of flags */
470 flags1_ptr = old_ptr;
471 unix_ptr = flags2_ptr = NULL;
472 BAM_DPRINTF(("%s: flags1 present: %s, unix, flags2 absent\n",
473 fcn, flags1_ptr));
477 * With dboot, unix is fixed and is at the beginning. We need to
478 * migrate flags1 and flags2
480 create:
481 if (entry->flags & BAM_ENTRY_FAILSAFE) {
482 (void) snprintf(new_arg, sizeof (new_arg), "%s",
483 DIRECT_BOOT_FAILSAFE_KERNEL);
484 } else {
485 (void) snprintf(new_arg, sizeof (new_arg), "%s",
486 DIRECT_BOOT_KERNEL);
488 BAM_DPRINTF(("%s: converted unix: %s\n", fcn, new_arg));
490 if (flags1_ptr != NULL) {
491 (void) strlcat(new_arg, " ", sizeof (new_arg));
492 (void) strlcat(new_arg, flags1_ptr, sizeof (new_arg));
495 if (flags2_ptr != NULL) {
496 (void) strlcat(new_arg, " ", sizeof (new_arg));
497 (void) strlcat(new_arg, flags2_ptr, sizeof (new_arg));
500 BAM_DPRINTF(("%s: converted unix with flags : %s\n", fcn, new_arg));
502 free(line->arg);
503 line->arg = s_strdup(new_arg);
504 update_line(line);
505 BAM_DPRINTF(("%s: converted line is: %s\n", fcn, line->line));
506 return (BAM_SUCCESS);
510 * Similar to above, except this time we're looking at a module line,
511 * which is quite a bit simpler.
513 * Under multiboot, the archive line is:
515 * module /platform/i86pc/boot_archive
517 * Under directboot, the archive line is:
519 * module$ /platform/i86pc/$ISADIR/boot_archive
521 * which may be specified exactly as either of:
523 * module /platform/i86pc/boot_archive
524 * module /platform/i86pc/amd64/boot_archive
526 * Under multiboot, the failsafe is:
528 * module /boot/x86.miniroot-safe
530 * Under dboot, the failsafe is:
532 * module$ /boot/$ISADIR/x86.miniroot-safe
534 * which may be specified exactly as either of:
536 * module /boot/x86.miniroot-safe
537 * module /boot/amd64/x86.miniroot-safe
539 static error_t
540 cvt_module_line(line_t *line, entry_t *entry)
542 const char *fcn = "cvt_module_line()";
544 BAM_DPRINTF(("%s: entered. arg: %s\n", fcn, line->line));
547 * We only convert multiboot to dboot and nothing else
549 if (!(entry->flags & BAM_ENTRY_MULTIBOOT)) {
550 BAM_DPRINTF(("%s: not MULTIBOOT, not converting\n", fcn));
551 return (BAM_SUCCESS);
554 if (entry->flags & BAM_ENTRY_FAILSAFE) {
555 if (strcmp(line->arg, FAILSAFE_ARCHIVE) == 0) {
556 BAM_DPRINTF(("%s: failsafe module line needs no "
557 "conversion: %s\n", fcn, line->arg));
558 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
559 return (BAM_SUCCESS);
561 } else if (strcmp(line->arg, MULTIBOOT_ARCHIVE) != 0) {
562 bam_error(_("module command on line %d not recognized.\n"),
563 line->lineNum);
564 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
565 return (BAM_MSG);
568 free(line->cmd);
569 free(line->arg);
570 line->cmd = s_strdup(menu_cmds[MODULE_DOLLAR_CMD]);
572 line->arg = s_strdup(entry->flags & BAM_ENTRY_FAILSAFE ?
573 FAILSAFE_ARCHIVE : DIRECT_BOOT_ARCHIVE);
575 update_line(line);
576 BAM_DPRINTF(("%s: converted module line is: %s\n", fcn, line->line));
577 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
578 return (BAM_SUCCESS);
581 static void
582 bam_warn_hand_entries(menu_t *mp, char *osroot)
584 int hand_num;
585 int hand_max;
586 int *hand_list;
587 int i;
588 entry_t *entry;
589 const char *fcn = "bam_warn_hand_entries()";
591 if (bam_force) {
593 * No warning needed, we are automatically converting
594 * the "hand" entries
596 BAM_DPRINTF(("%s: force specified, no warnings about hand "
597 "entries\n", fcn));
598 return;
601 hand_num = 0;
602 hand_max = BAM_ENTRY_NUM;
603 hand_list = s_calloc(1, hand_max);
605 for (entry = mp->entries; entry; entry = entry->next) {
606 if (entry->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU))
607 continue;
608 BAM_DPRINTF(("%s: found hand entry #: %d\n", fcn,
609 entry->entryNum));
610 if (++hand_num > hand_max) {
611 hand_max *= 2;
612 hand_list = s_realloc(hand_list,
613 hand_max * sizeof (int));
615 hand_list[hand_num - 1] = entry->entryNum;
618 bam_error(_("bootadm(1M) will only upgrade GRUB menu entries added "
619 "by \nbootadm(1M) or lu(1M). The following entries on %s will "
620 "not be upgraded.\nFor details on manually updating entries, "
621 "see %s\n"), osroot, MENU_URL(osroot));
622 bam_print_stderr("Entry Number%s: ", (hand_num > 1) ?
623 "s" : "");
624 for (i = 0; i < hand_num; i++) {
625 bam_print_stderr("%d ", hand_list[i]);
627 bam_print_stderr("\n");
630 static entry_t *
631 find_matching_entry(
632 entry_t *estart,
633 char *grubsign,
634 char *grubroot,
635 int root_opt)
637 entry_t *entry;
638 line_t *line;
639 char opt[10];
640 const char *fcn = "find_matching_entry()";
642 assert(grubsign);
643 assert(root_opt == 0 || root_opt == 1);
645 (void) snprintf(opt, sizeof (opt), "%d", root_opt);
646 BAM_DPRINTF(("%s: entered. args: %s %s %s\n", fcn, grubsign,
647 grubroot, opt));
649 for (entry = estart; entry; entry = entry->next) {
651 if (!(entry->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU)) &&
652 !bam_force) {
653 BAM_DPRINTF(("%s: skipping hand entry #: %d\n",
654 fcn, entry->entryNum));
655 continue;
658 if (entry->flags & BAM_ENTRY_ROOT) {
659 for (line = entry->start; line; line = line->next) {
660 if (line->cmd == NULL || line->arg == NULL) {
661 if (line == entry->end) {
662 BAM_DPRINTF(("%s: entry has "
663 "ended\n", fcn));
664 break;
665 } else {
666 BAM_DPRINTF(("%s: skipping "
667 "NULL line\n", fcn));
668 continue;
671 if (strcmp(line->cmd, menu_cmds[ROOT_CMD])
672 == 0 && strcmp(line->arg, grubroot) == 0) {
673 BAM_DPRINTF(("%s: found matching root "
674 "line: %s,%s\n", fcn,
675 line->line, grubsign));
676 return (entry);
678 if (line == entry->end) {
679 BAM_DPRINTF(("%s: entry has ended\n",
680 fcn));
681 break;
684 } else if (entry->flags & BAM_ENTRY_FINDROOT) {
685 for (line = entry->start; line; line = line->next) {
686 if (line->cmd == NULL || line->arg == NULL) {
687 if (line == entry->end) {
688 BAM_DPRINTF(("%s: entry has "
689 "ended\n", fcn));
690 break;
691 } else {
692 BAM_DPRINTF(("%s: skipping "
693 "NULL line\n", fcn));
694 continue;
697 if (strcmp(line->cmd, menu_cmds[FINDROOT_CMD])
698 == 0 && strcmp(line->arg, grubsign) == 0) {
699 BAM_DPRINTF(("%s: found matching "
700 "findroot line: %s,%s\n", fcn,
701 line->line, grubsign));
702 return (entry);
704 if (line == entry->end) {
705 BAM_DPRINTF(("%s: entry has ended\n",
706 fcn));
707 break;
710 } else if (root_opt) {
711 /* Neither root nor findroot */
712 BAM_DPRINTF(("%s: no root or findroot and root is "
713 "opt: %d\n", fcn, entry->entryNum));
714 return (entry);
718 BAM_DPRINTF(("%s: no matching entry found\n", fcn));
719 return (NULL);
723 * The following is a set of routines that attempt to convert the
724 * menu entries for the supplied osroot into a format compatible
725 * with the GRUB installation on osroot.
727 * Each of these conversion routines make no assumptions about
728 * the current state of the menu entry, it does its best to
729 * convert the menu entry to the new state. In the process
730 * we may either upgrade or downgrade.
732 * We don't make any heroic efforts at conversion. It is better
733 * to be conservative and bail out at the first sign of error. We will
734 * in such cases, point the user at the knowledge-base article
735 * so that they can upgrade manually.
737 static error_t
738 bam_add_findroot(menu_t *mp, char *grubsign, char *grubroot, int root_opt)
740 entry_t *entry;
741 line_t *line;
742 line_t *newlp;
743 int update_num;
744 char linebuf[PATH_MAX];
745 const char *fcn = "bam_add_findroot()";
747 update_num = 0;
749 bam_print(_("converting entries to findroot...\n"));
751 entry = find_matching_entry(mp->entries, grubsign, grubroot, root_opt);
752 while (entry != NULL) {
753 if (entry->flags & BAM_ENTRY_FINDROOT) {
754 /* already converted */
755 BAM_DPRINTF(("%s: entry %d already converted to "
756 "findroot\n", fcn, entry->entryNum));
757 entry = find_matching_entry(entry->next, grubsign,
758 grubroot, root_opt);
759 continue;
761 for (line = entry->start; line; line = line->next) {
762 if (line->cmd == NULL || line->arg == NULL) {
763 if (line == entry->end) {
764 BAM_DPRINTF(("%s: entry has ended\n",
765 fcn));
766 break;
767 } else {
768 BAM_DPRINTF(("%s: skipping NULL line\n",
769 fcn));
770 continue;
773 if (strcmp(line->cmd, menu_cmds[TITLE_CMD]) == 0) {
774 newlp = s_calloc(1, sizeof (line_t));
775 newlp->cmd = s_strdup(menu_cmds[FINDROOT_CMD]);
776 newlp->sep = s_strdup(" ");
777 newlp->arg = s_strdup(grubsign);
778 (void) snprintf(linebuf, sizeof (linebuf),
779 "%s%s%s", newlp->cmd, newlp->sep,
780 newlp->arg);
781 newlp->line = s_strdup(linebuf);
782 bam_add_line(mp, entry, line, newlp);
783 update_num = 1;
784 entry->flags &= ~BAM_ENTRY_ROOT;
785 entry->flags |= BAM_ENTRY_FINDROOT;
786 BAM_DPRINTF(("%s: added findroot line: %s\n",
787 fcn, newlp->line));
788 line = newlp;
790 if (strcmp(line->cmd, menu_cmds[ROOT_CMD]) == 0) {
791 BAM_DPRINTF(("%s: freeing root line: %s\n",
792 fcn, line->line));
793 unlink_line(mp, line);
794 line_free(line);
796 if (line == entry->end) {
797 BAM_DPRINTF(("%s: entry has ended\n", fcn));
798 break;
801 entry = find_matching_entry(entry->next, grubsign, grubroot,
802 root_opt);
805 if (update_num) {
806 BAM_DPRINTF(("%s: updated numbering\n", fcn));
807 update_numbering(mp);
810 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
811 return (BAM_SUCCESS);
814 static error_t
815 bam_add_hv(menu_t *mp, char *grubsign, char *grubroot, int root_opt)
817 entry_t *entry;
818 const char *fcn = "bam_add_hv()";
820 bam_print(_("adding xVM entries...\n"));
822 entry = find_matching_entry(mp->entries, grubsign, grubroot, root_opt);
823 while (entry != NULL) {
824 if (entry->flags & BAM_ENTRY_HV) {
825 BAM_DPRINTF(("%s: entry %d already converted to "
826 "xvm HV\n", fcn, entry->entryNum));
827 return (BAM_SUCCESS);
829 entry = find_matching_entry(entry->next, grubsign, grubroot,
830 root_opt);
833 (void) add_boot_entry(mp, NEW_HV_ENTRY, grubsign, XEN_MENU,
834 XEN_KERNEL_MODULE_LINE, DIRECT_BOOT_ARCHIVE, NULL);
836 BAM_DPRINTF(("%s: added xVM HV entry via add_boot_entry()\n", fcn));
838 update_numbering(mp);
840 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
842 return (BAM_SUCCESS);
845 static error_t
846 bam_add_dboot(
847 menu_t *mp,
848 char *osroot,
849 char *grubsign,
850 char *grubroot,
851 int root_opt)
853 int msg = 0;
854 entry_t *entry;
855 line_t *line;
856 error_t ret;
857 const char *fcn = "bam_add_dboot()";
859 bam_print(_("converting entries to dboot...\n"));
861 entry = find_matching_entry(mp->entries, grubsign, grubroot, root_opt);
862 while (entry != NULL) {
863 for (line = entry->start; line; line = line->next) {
864 if (line->cmd == NULL || line->arg == NULL) {
865 if (line == entry->end) {
866 BAM_DPRINTF(("%s: entry has ended\n",
867 fcn));
868 break;
869 } else {
870 BAM_DPRINTF(("%s: skipping NULL line\n",
871 fcn));
872 continue;
877 * If we have a kernel$ command, assume it
878 * is dboot already. If it is not a dboot
879 * entry, something funny is going on and
880 * we will leave it alone
882 if (strcmp(line->cmd, menu_cmds[KERNEL_CMD]) == 0) {
883 ret = cvt_kernel_line(line, osroot, entry);
884 INJECT_ERROR1("ADD_DBOOT_KERN_ERR",
885 ret = BAM_ERROR);
886 INJECT_ERROR1("ADD_DBOOT_KERN_MSG",
887 ret = BAM_MSG);
888 if (ret == BAM_ERROR) {
889 BAM_DPRINTF(("%s: cvt_kernel_line() "
890 "failed\n", fcn));
891 return (ret);
892 } else if (ret == BAM_MSG) {
893 msg = 1;
894 BAM_DPRINTF(("%s: BAM_MSG returned "
895 "from cvt_kernel_line()\n", fcn));
898 if (strcmp(line->cmd, menu_cmds[MODULE_CMD]) == 0) {
899 ret = cvt_module_line(line, entry);
900 INJECT_ERROR1("ADD_DBOOT_MOD_ERR",
901 ret = BAM_ERROR);
902 INJECT_ERROR1("ADD_DBOOT_MOD_MSG",
903 ret = BAM_MSG);
904 if (ret == BAM_ERROR) {
905 BAM_DPRINTF(("%s: cvt_module_line() "
906 "failed\n", fcn));
907 return (ret);
908 } else if (ret == BAM_MSG) {
909 BAM_DPRINTF(("%s: BAM_MSG returned "
910 "from cvt_module_line()\n", fcn));
911 msg = 1;
915 if (line == entry->end) {
916 BAM_DPRINTF(("%s: entry has ended\n", fcn));
917 break;
920 entry = find_matching_entry(entry->next, grubsign, grubroot,
921 root_opt);
924 ret = msg ? BAM_MSG : BAM_SUCCESS;
925 BAM_DPRINTF(("%s: returning ret = %d\n", fcn, ret));
926 return (ret);
929 /*ARGSUSED*/
930 error_t
931 upgrade_menu(menu_t *mp, char *osroot, char *menu_root)
933 char *osdev;
934 char *grubsign;
935 char *grubroot;
936 int ret1;
937 int ret2;
938 int ret3;
939 const char *fcn = "upgrade_menu()";
941 assert(osroot);
942 assert(menu_root);
944 BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, osroot, menu_root));
947 * We only support upgrades. Xen may not be present
948 * on smaller metaclusters so we don't check for that.
950 if (bam_is_findroot != BAM_FINDROOT_PRESENT ||
951 bam_direct != BAM_DIRECT_DBOOT) {
952 bam_error(_("automated downgrade of GRUB menu to older "
953 "version not supported.\n"));
954 return (BAM_ERROR);
958 * First get the GRUB signature
960 osdev = get_special(osroot);
961 INJECT_ERROR1("UPGRADE_OSDEV", osdev = NULL);
962 if (osdev == NULL) {
963 bam_error(_("cant find special file for mount-point %s\n"),
964 osroot);
965 return (BAM_ERROR);
968 grubsign = get_grubsign(osroot, osdev);
969 INJECT_ERROR1("UPGRADE_GRUBSIGN", grubsign = NULL);
970 if (grubsign == NULL) {
971 free(osdev);
972 bam_error(_("cannot find GRUB signature for %s\n"), osroot);
973 return (BAM_ERROR);
976 /* not fatal if we can't get grubroot */
977 grubroot = get_grubroot(osroot, osdev, menu_root);
978 INJECT_ERROR1("UPGRADE_GRUBROOT", grubroot = NULL);
980 free(osdev);
982 ret1 = bam_add_findroot(mp, grubsign,
983 grubroot, root_optional(osroot, menu_root));
984 INJECT_ERROR1("UPGRADE_ADD_FINDROOT", ret1 = BAM_ERROR);
985 if (ret1 == BAM_ERROR)
986 goto abort;
988 if (bam_is_hv == BAM_HV_PRESENT) {
989 ret2 = bam_add_hv(mp, grubsign, grubroot,
990 root_optional(osroot, menu_root));
991 INJECT_ERROR1("UPGRADE_ADD_HV", ret2 = BAM_ERROR);
992 if (ret2 == BAM_ERROR)
993 goto abort;
994 } else
995 ret2 = BAM_SUCCESS;
997 ret3 = bam_add_dboot(mp, osroot, grubsign,
998 grubroot, root_optional(osroot, menu_root));
999 INJECT_ERROR1("UPGRADE_ADD_DBOOT", ret3 = BAM_ERROR);
1000 if (ret3 == BAM_ERROR)
1001 goto abort;
1003 if (ret1 == BAM_MSG || ret2 == BAM_MSG || ret3 == BAM_MSG) {
1004 bam_error(_("one or more GRUB menu entries were not "
1005 "automatically upgraded\nFor details on manually "
1006 "updating entries, see %s\n"), MENU_URL(osroot));
1007 } else {
1008 bam_warn_hand_entries(mp, osroot);
1011 free(grubsign);
1013 BAM_DPRINTF(("%s: returning ret = %d\n", fcn, BAM_WRITE));
1014 return (BAM_WRITE);
1016 abort:
1017 free(grubsign);
1018 bam_error(_("error upgrading GRUB menu entries on %s. Aborting.\n"
1019 "For details on manually updating entries, see %s\n"), osroot,
1020 MENU_URL(osroot));
1021 return (BAM_ERROR);