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