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