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 * Copyright 2017 Toomas Soome <tsoome@me.com> 25 */ 26 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> 37 38 #include <sys/mman.h> 39 #include <sys/elf.h> 40 #include <sys/multiboot.h> 41 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 (void) snprintf(path, sizeof (path), "%s/%s", 61 osroot, "boot/grub/capability"); 62 63 if (stat(path, &sb) == -1) { 64 bam_is_findroot = BAM_FINDROOT_ABSENT; 65 BAM_DPRINTF(("%s: findroot capability absent\n", fcn)); 66 return; 67 } 68 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; 76 } 77 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; 83 } 84 if (strcmp(buf, "dboot") == 0) { 85 BAM_DPRINTF(("%s: dboot capability present\n", fcn)); 86 dboot = 1; 87 } 88 } 89 90 assert(dboot); 91 92 if (bam_is_findroot == BAM_FINDROOT_UNKNOWN) { 93 bam_is_findroot = BAM_FINDROOT_ABSENT; 94 BAM_DPRINTF(("%s: findroot capability absent\n", fcn)); 95 } 96 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)); 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 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()"; 119 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); 125 } 126 127 /* 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. 135 */ 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); 145 } 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); 152 } 153 154 /* 155 * Verify that this is a sane unix at least 8192 bytes in length 156 */ 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); 161 } 162 163 /* 164 * mmap the first 8K 165 */ 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); 173 } 174 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); 180 } 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); 185 } 186 187 /* 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. 193 */ 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; 201 } 202 } 203 (void) munmap(image, 8192); 204 (void) close(fd); 205 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)); 212 } 213 } else { 214 BAM_DPRINTF(("%s: is MULTIBOOT unix\n", fcn)); 215 } 216 217 /* Not a fatal error if this fails */ 218 get_findroot_cap(osroot); 219 220 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn)); 221 return (BAM_SUCCESS); 222 } 223 224 #define INST_RELEASE "var/sadm/system/admin/INST_RELEASE" 225 226 /* 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. 230 */ 231 static int 232 is_bfu_system(const char *root) 233 { 234 static int is_bfu = -1; 235 char path[PATH_MAX]; 236 struct stat sb; 237 const char *fcn = "is_bfu_system()"; 238 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); 243 } 244 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)); 252 } 253 return (is_bfu); 254 } 255 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") 259 260 /* 261 * Simply allocate a new line and copy in cmd + sep + arg 262 */ 263 void 264 update_line(line_t *linep) 265 { 266 size_t size; 267 const char *fcn = "update_line()"; 268 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)); 276 } 277 278 static char * 279 skip_wspace(char *ptr) 280 { 281 const char *fcn = "skip_wspace()"; 282 283 INJECT_ERROR1("SKIP_WSPACE", ptr = NULL); 284 if (ptr == NULL) { 285 BAM_DPRINTF(("%s: NULL ptr\n", fcn)); 286 return (NULL); 287 } 288 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; 294 } 295 296 ptr = (*ptr == '\0' ? NULL : ptr); 297 298 BAM_DPRINTF(("%s: ptr on exit: %s\n", fcn, ptr ? ptr : "NULL")); 299 300 return (ptr); 301 } 302 303 static char * 304 rskip_bspace(char *bound, char *ptr) 305 { 306 const char *fcn = "rskip_bspace()"; 307 assert(bound); 308 assert(ptr); 309 assert(bound <= ptr); 310 assert(*bound != ' ' && *bound != '\t' && *bound != '\n'); 311 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; 316 } 317 318 BAM_DPRINTF(("%s: ptr on exit: %s\n", fcn, ptr)); 319 return (ptr); 320 } 321 322 /* 323 * The parse_kernel_line function examines a menu.lst kernel line. For 324 * multiboot, this is: 325 * 326 * kernel <multiboot path> <flags1> <kernel path> <flags2> 327 * 328 * <multiboot path> is either /platform/i86pc/multiboot or /boot/multiboot 329 * 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. 335 * 336 * <flags*> are anything that doesn't fit either of the above - these will be 337 * copied over. 338 * 339 * For direct boot, the defaults are 340 * 341 * kernel$ <kernel path> <flags> 342 * 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 350 * 351 * If <kernel path> is any of the last four, the command may also be "kernel". 352 * 353 * <flags> is anything that isn't <kernel path>. 354 * 355 * This function is only called to convert a multiboot entry to a dboot entry 356 * 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. 361 */ 362 static error_t 363 cvt_kernel_line(line_t *line, const char *osroot, entry_t *entry) 364 { 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()"; 374 375 BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, line->line, osroot)); 376 377 /* 378 * We only convert multiboot to dboot and nothing else. 379 */ 380 if (!(entry->flags & BAM_ENTRY_MULTIBOOT)) { 381 BAM_DPRINTF(("%s: not MULTIBOOT, not converting\n", fcn)); 382 return (BAM_SUCCESS); 383 } 384 385 if (entry->flags & BAM_ENTRY_FAILSAFE) { 386 /* 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. 392 */ 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); 403 } 404 BAM_DPRINTF(("%s: no FAILSAFE unix, not converting\n", 405 fcn)); 406 return (BAM_SUCCESS); 407 } 408 } 409 410 /* 411 * Make sure we have the correct cmd 412 */ 413 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)); 417 418 assert(sizeof (linebuf) > strlen(line->arg) + 32); 419 (void) strlcpy(linebuf, line->arg, sizeof (linebuf)); 420 421 old_ptr = strpbrk(linebuf, " \t\n"); 422 old_ptr = skip_wspace(old_ptr); 423 if (old_ptr == NULL) { 424 /* 425 * only multiboot and nothing else 426 * i.e. flags1 = unix = flags2 = NULL 427 */ 428 flags1_ptr = unix_ptr = flags2_ptr = NULL; 429 BAM_DPRINTF(("%s: NULL flags1, unix, flags2\n", fcn)) 430 goto create; 431 } 432 433 /* 434 * 435 * old_ptr is either at "flags1" or "unix" 436 */ 437 if ((unix_ptr = strstr(old_ptr, "/unix")) != NULL) { 438 439 /* 440 * There is a unix. 441 */ 442 BAM_DPRINTF(("%s: unix present\n", fcn)); 443 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)); 452 } 453 454 /* see if there is a flags1 before unix */ 455 unix_ptr = rskip_bspace(old_ptr, unix_ptr); 456 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)); 466 } 467 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)); 474 } 475 476 /* 477 * With dboot, unix is fixed and is at the beginning. We need to 478 * migrate flags1 and flags2 479 */ 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); 487 } 488 BAM_DPRINTF(("%s: converted unix: %s\n", fcn, new_arg)); 489 490 if (flags1_ptr != NULL) { 491 (void) strlcat(new_arg, " ", sizeof (new_arg)); 492 (void) strlcat(new_arg, flags1_ptr, sizeof (new_arg)); 493 } 494 495 if (flags2_ptr != NULL) { 496 (void) strlcat(new_arg, " ", sizeof (new_arg)); 497 (void) strlcat(new_arg, flags2_ptr, sizeof (new_arg)); 498 } 499 500 BAM_DPRINTF(("%s: converted unix with flags : %s\n", fcn, new_arg)); 501 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); 507 } 508 509 /* 510 * Similar to above, except this time we're looking at a module line, 511 * which is quite a bit simpler. 512 * 513 * Under multiboot, the archive line is: 514 * 515 * module /platform/i86pc/boot_archive 516 * 517 * Under directboot, the archive line is: 518 * 519 * module$ /platform/i86pc/$ISADIR/boot_archive 520 * 521 * which may be specified exactly as either of: 522 * 523 * module /platform/i86pc/boot_archive 524 * module /platform/i86pc/amd64/boot_archive 525 * 526 * Under multiboot, the failsafe is: 527 * 528 * module /boot/x86.miniroot-safe 529 * 530 * Under dboot, the failsafe is: 531 * 532 * module$ /boot/$ISADIR/x86.miniroot-safe 533 * 534 * which may be specified exactly as either of: 535 * 536 * module /boot/x86.miniroot-safe 537 * module /boot/amd64/x86.miniroot-safe 538 */ 539 static error_t 540 cvt_module_line(line_t *line, entry_t *entry) 541 { 542 const char *fcn = "cvt_module_line()"; 543 544 BAM_DPRINTF(("%s: entered. arg: %s\n", fcn, line->line)); 545 546 /* 547 * We only convert multiboot to dboot and nothing else 548 */ 549 if (!(entry->flags & BAM_ENTRY_MULTIBOOT)) { 550 BAM_DPRINTF(("%s: not MULTIBOOT, not converting\n", fcn)); 551 return (BAM_SUCCESS); 552 } 553 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); 560 } 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); 566 } 567 568 free(line->cmd); 569 free(line->arg); 570 line->cmd = s_strdup(menu_cmds[MODULE_DOLLAR_CMD]); 571 572 line->arg = s_strdup(entry->flags & BAM_ENTRY_FAILSAFE ? 573 FAILSAFE_ARCHIVE : DIRECT_BOOT_ARCHIVE); 574 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); 579 } 580 581 static void 582 bam_warn_hand_entries(menu_t *mp, char *osroot) 583 { 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()"; 590 591 if (bam_force) { 592 /* 593 * No warning needed, we are automatically converting 594 * the "hand" entries 595 */ 596 BAM_DPRINTF(("%s: force specified, no warnings about hand " 597 "entries\n", fcn)); 598 return; 599 } 600 601 hand_num = 0; 602 hand_max = BAM_ENTRY_NUM; 603 hand_list = s_calloc(1, hand_max); 604 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)); 614 } 615 hand_list[hand_num - 1] = entry->entryNum; 616 } 617 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]); 626 } 627 bam_print_stderr("\n"); 628 } 629 630 static entry_t * 631 find_matching_entry( 632 entry_t *estart, 633 char *grubsign, 634 char *grubroot, 635 int root_opt) 636 { 637 entry_t *entry; 638 line_t *line; 639 char opt[10]; 640 const char *fcn = "find_matching_entry()"; 641 642 assert(grubsign); 643 assert(root_opt == 0 || root_opt == 1); 644 645 (void) snprintf(opt, sizeof (opt), "%d", root_opt); 646 BAM_DPRINTF(("%s: entered. args: %s %s %s\n", fcn, grubsign, 647 grubroot, opt)); 648 649 for (entry = estart; entry; entry = entry->next) { 650 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; 656 } 657 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; 669 } 670 } 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); 677 } 678 if (line == entry->end) { 679 BAM_DPRINTF(("%s: entry has ended\n", 680 fcn)); 681 break; 682 } 683 } 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; 695 } 696 } 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); 703 } 704 if (line == entry->end) { 705 BAM_DPRINTF(("%s: entry has ended\n", 706 fcn)); 707 break; 708 } 709 } 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); 715 } 716 } 717 718 BAM_DPRINTF(("%s: no matching entry found\n", fcn)); 719 return (NULL); 720 } 721 722 /* 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. 726 * 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. 731 * 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. 736 */ 737 static error_t 738 bam_add_findroot(menu_t *mp, char *grubsign, char *grubroot, int root_opt) 739 { 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()"; 746 747 update_num = 0; 748 749 bam_print(_("converting entries to findroot...\n")); 750 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; 760 } 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; 771 } 772 } 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; 789 } 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); 795 } 796 if (line == entry->end) { 797 BAM_DPRINTF(("%s: entry has ended\n", fcn)); 798 break; 799 } 800 } 801 entry = find_matching_entry(entry->next, grubsign, grubroot, 802 root_opt); 803 } 804 805 if (update_num) { 806 BAM_DPRINTF(("%s: updated numbering\n", fcn)); 807 update_numbering(mp); 808 } 809 810 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn)); 811 return (BAM_SUCCESS); 812 } 813 814 static error_t 815 bam_add_hv(menu_t *mp, char *grubsign, char *grubroot, int root_opt) 816 { 817 entry_t *entry; 818 const char *fcn = "bam_add_hv()"; 819 820 bam_print(_("adding xVM entries...\n")); 821 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); 828 } 829 entry = find_matching_entry(entry->next, grubsign, grubroot, 830 root_opt); 831 } 832 833 (void) add_boot_entry(mp, NEW_HV_ENTRY, grubsign, XEN_MENU, 834 XEN_KERNEL_MODULE_LINE, DIRECT_BOOT_ARCHIVE, NULL); 835 836 BAM_DPRINTF(("%s: added xVM HV entry via add_boot_entry()\n", fcn)); 837 838 update_numbering(mp); 839 840 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn)); 841 842 return (BAM_SUCCESS); 843 } 844 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) 852 { 853 int msg = 0; 854 entry_t *entry; 855 line_t *line; 856 error_t ret; 857 const char *fcn = "bam_add_dboot()"; 858 859 bam_print(_("converting entries to dboot...\n")); 860 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; 873 } 874 } 875 876 /* 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 881 */ 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)); 896 } 897 } 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; 912 } 913 } 914 915 if (line == entry->end) { 916 BAM_DPRINTF(("%s: entry has ended\n", fcn)); 917 break; 918 } 919 } 920 entry = find_matching_entry(entry->next, grubsign, grubroot, 921 root_opt); 922 } 923 924 ret = msg ? BAM_MSG : BAM_SUCCESS; 925 BAM_DPRINTF(("%s: returning ret = %d\n", fcn, ret)); 926 return (ret); 927 } 928 929 /*ARGSUSED*/ 930 error_t 931 upgrade_menu(menu_t *mp, char *osroot, char *menu_root) 932 { 933 char *osdev; 934 char *grubsign; 935 char *grubroot; 936 int ret1; 937 int ret2; 938 int ret3; 939 const char *fcn = "upgrade_menu()"; 940 941 assert(osroot); 942 assert(menu_root); 943 944 BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, osroot, menu_root)); 945 946 /* 947 * We only support upgrades. Xen may not be present 948 * on smaller metaclusters so we don't check for that. 949 */ 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); 955 } 956 957 /* 958 * First get the GRUB signature 959 */ 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); 966 } 967 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); 974 } 975 976 /* not fatal if we can't get grubroot */ 977 grubroot = get_grubroot(osroot, osdev, menu_root); 978 INJECT_ERROR1("UPGRADE_GRUBROOT", grubroot = NULL); 979 980 free(osdev); 981 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; 987 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; 996 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; 1002 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); 1009 } 1010 1011 free(grubsign); 1012 1013 BAM_DPRINTF(("%s: returning ret = %d\n", fcn, BAM_WRITE)); 1014 return (BAM_WRITE); 1015 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); 1022 }