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