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 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <stdio.h> 29 #include <errno.h> 30 #include <stdlib.h> 31 #include <string.h> 32 #include <unistd.h> 33 #include <sys/types.h> 34 #include <sys/stat.h> 35 #include <limits.h> 36 #include <fcntl.h> 37 #include <strings.h> 38 39 #include <sys/mman.h> 40 #include <sys/elf.h> 41 #include <sys/multiboot.h> 42 43 #include "message.h" 44 #include "bootadm.h" 45 46 direct_or_multi_t bam_direct = BAM_DIRECT_NOT_SET; 47 hv_t bam_is_hv = BAM_HV_UNKNOWN; 48 findroot_t bam_is_findroot = BAM_FINDROOT_UNKNOWN; 49 50 static void 51 get_findroot_cap(const char *osroot) 52 { 53 FILE *fp; 54 char path[PATH_MAX]; 55 char buf[BAM_MAXLINE]; 56 struct stat sb; 57 int dboot; 58 int xVM; 59 int error; 60 int ret; 61 const char *fcn = "get_findroot_cap()"; 62 63 assert(is_grub(osroot)); 64 65 (void) snprintf(path, sizeof (path), "%s/%s", 66 osroot, "boot/grub/capability"); 67 68 if (stat(path, &sb) == -1) { 69 bam_is_findroot = BAM_FINDROOT_ABSENT; 70 BAM_DPRINTF((D_FINDROOT_ABSENT, fcn)); 71 return; 72 } 73 74 fp = fopen(path, "r"); 75 error = errno; 76 INJECT_ERROR1("GET_CAP_FINDROOT_FOPEN", fp = NULL); 77 if (fp == NULL) { 78 bam_error(OPEN_FAIL, path, strerror(error)); 79 return; 80 } 81 82 dboot = xVM = 0; 83 while (s_fgets(buf, sizeof (buf), fp) != NULL) { 84 if (strcmp(buf, "findroot") == 0) { 85 BAM_DPRINTF((D_FINDROOT_PRESENT, fcn)); 86 bam_is_findroot = BAM_FINDROOT_PRESENT; 87 } 88 if (strcmp(buf, "dboot") == 0) { 89 BAM_DPRINTF((D_DBOOT_PRESENT, fcn)); 90 dboot = 1; 91 } 92 if (strcmp(buf, "xVM") == 0) { 93 BAM_DPRINTF((D_XVM_PRESENT, fcn)); 94 xVM = 1; 95 } 96 } 97 98 assert(dboot); 99 assert(xVM); 100 101 if (bam_is_findroot == BAM_FINDROOT_UNKNOWN) { 102 bam_is_findroot = BAM_FINDROOT_ABSENT; 103 BAM_DPRINTF((D_FINDROOT_ABSENT, fcn)); 104 } 105 out: 106 ret = fclose(fp); 107 error = errno; 108 INJECT_ERROR1("GET_CAP_FINDROOT_FCLOSE", ret = 1); 109 if (ret != 0) { 110 bam_error(CLOSE_FAIL, path, strerror(error)); 111 } 112 } 113 114 error_t 115 get_boot_cap(const char *osroot) 116 { 117 char fname[PATH_MAX]; 118 char *image; 119 uchar_t *ident; 120 int fd; 121 int m; 122 multiboot_header_t *mbh; 123 struct stat sb; 124 int error; 125 const char *fcn = "get_boot_cap()"; 126 127 if (is_sparc()) { 128 /* there is no non dboot sparc new-boot */ 129 bam_direct = BAM_DIRECT_DBOOT; 130 BAM_DPRINTF((D_IS_SPARC_DBOOT, fcn)); 131 return (BAM_SUCCESS); 132 } 133 134 if (!is_grub(osroot)) { 135 bam_error(NOT_GRUB_ROOT, osroot); 136 return (BAM_ERROR); 137 } 138 139 (void) snprintf(fname, PATH_MAX, "%s/%s", osroot, 140 "platform/i86pc/kernel/unix"); 141 fd = open(fname, O_RDONLY); 142 error = errno; 143 INJECT_ERROR1("GET_CAP_UNIX_OPEN", fd = -1); 144 if (fd < 0) { 145 bam_error(OPEN_FAIL, fname, strerror(error)); 146 return (BAM_ERROR); 147 } 148 149 /* 150 * Verify that this is a sane unix at least 8192 bytes in length 151 */ 152 if (fstat(fd, &sb) == -1 || sb.st_size < 8192) { 153 (void) close(fd); 154 bam_error(INVALID_BINARY, fname); 155 return (BAM_ERROR); 156 } 157 158 /* 159 * mmap the first 8K 160 */ 161 image = mmap(NULL, 8192, PROT_READ, MAP_SHARED, fd, 0); 162 error = errno; 163 INJECT_ERROR1("GET_CAP_MMAP", image = MAP_FAILED); 164 if (image == MAP_FAILED) { 165 bam_error(MMAP_FAIL, fname, strerror(error)); 166 return (BAM_ERROR); 167 } 168 169 ident = (uchar_t *)image; 170 if (ident[EI_MAG0] != ELFMAG0 || ident[EI_MAG1] != ELFMAG1 || 171 ident[EI_MAG2] != ELFMAG2 || ident[EI_MAG3] != ELFMAG3) { 172 bam_error(NOT_ELF_FILE, fname); 173 return (BAM_ERROR); 174 } 175 if (ident[EI_CLASS] != ELFCLASS32) { 176 bam_error(WRONG_ELF_CLASS, fname, ident[EI_CLASS]); 177 return (BAM_ERROR); 178 } 179 180 /* 181 * The GRUB multiboot header must be 32-bit aligned and completely 182 * contained in the 1st 8K of the file. If the unix binary has 183 * a multiboot header, then it is a 'dboot' kernel. Otherwise, 184 * this kernel must be booted via multiboot -- we call this a 185 * 'multiboot' kernel. 186 */ 187 bam_direct = BAM_DIRECT_MULTIBOOT; 188 for (m = 0; m < 8192 - sizeof (multiboot_header_t); m += 4) { 189 mbh = (void *)(image + m); 190 if (mbh->magic == MB_HEADER_MAGIC) { 191 BAM_DPRINTF((D_IS_DBOOT, fcn)); 192 bam_direct = BAM_DIRECT_DBOOT; 193 break; 194 } 195 } 196 (void) munmap(image, 8192); 197 (void) close(fd); 198 199 INJECT_ERROR1("GET_CAP_MULTIBOOT", bam_direct = BAM_DIRECT_MULTIBOOT); 200 if (bam_direct == BAM_DIRECT_DBOOT) { 201 (void) snprintf(fname, PATH_MAX, "%s/%s", osroot, XEN_32); 202 if (stat(fname, &sb) == 0) { 203 bam_is_hv = BAM_HV_PRESENT; 204 BAM_DPRINTF((D_IS_XVM, fcn)); 205 } else { 206 bam_is_hv = BAM_HV_NO; 207 BAM_DPRINTF((D_IS_NOT_XVM, fcn)); 208 } 209 } else { 210 BAM_DPRINTF((D_IS_MULTIBOOT, fcn)); 211 } 212 213 /* Not a fatal error if this fails */ 214 get_findroot_cap(osroot); 215 216 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 217 return (BAM_SUCCESS); 218 } 219 220 #define INST_RELEASE "var/sadm/system/admin/INST_RELEASE" 221 222 /* 223 * Return true if root has been bfu'ed. bfu will blow away 224 * var/sadm/system/admin/INST_RELEASE, so if it's still there, we can 225 * assume the system has not been bfu'ed. 226 */ 227 static int 228 is_bfu_system(const char *root) 229 { 230 static int is_bfu = -1; 231 char path[PATH_MAX]; 232 struct stat sb; 233 const char *fcn = "is_bfu_system()"; 234 235 if (is_bfu != -1) { 236 BAM_DPRINTF((D_ALREADY_BFU_TEST, fcn, is_bfu ? "" : "NOT")); 237 return (is_bfu); 238 } 239 240 (void) snprintf(path, sizeof (path), "%s/%s", root, INST_RELEASE); 241 if (stat(path, &sb) != 0) { 242 is_bfu = 1; 243 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 244 } else { 245 is_bfu = 0; 246 BAM_DPRINTF((D_RETURN_FAILURE, fcn)); 247 } 248 return (is_bfu); 249 } 250 251 #define MENU_URL(root) (is_bfu_system(root) ? \ 252 "http://www.sun.com/msg/SUNOS-8000-CF" : \ 253 "http://www.sun.com/msg/SUNOS-8000-AK") 254 255 /* 256 * Simply allocate a new line and copy in cmd + sep + arg 257 */ 258 void 259 update_line(line_t *linep) 260 { 261 size_t size; 262 const char *fcn = "update_line()"; 263 264 BAM_DPRINTF((D_UPDATE_LINE_BEFORE, fcn, linep->line)); 265 free(linep->line); 266 size = strlen(linep->cmd) + strlen(linep->sep) + strlen(linep->arg) + 1; 267 linep->line = s_calloc(1, size); 268 (void) snprintf(linep->line, size, "%s%s%s", linep->cmd, linep->sep, 269 linep->arg); 270 BAM_DPRINTF((D_UPDATE_LINE_AFTER, fcn, linep->line)); 271 } 272 273 static char * 274 skip_wspace(char *ptr) 275 { 276 const char *fcn = "skip_wspace()"; 277 278 INJECT_ERROR1("SKIP_WSPACE", ptr = NULL); 279 if (ptr == NULL) { 280 BAM_DPRINTF((D_SKIP_WSPACE_PTR_NULL, fcn)); 281 return (NULL); 282 } 283 284 BAM_DPRINTF((D_SKIP_WSPACE_ENTRY_PTR, fcn, ptr)); 285 for (; *ptr != '\0'; ptr++) { 286 if ((*ptr != ' ') && (*ptr != '\t') && 287 (*ptr != '\n')) 288 break; 289 } 290 291 ptr = (*ptr == '\0' ? NULL : ptr); 292 293 BAM_DPRINTF((D_SKIP_WSPACE_EXIT_PTR, fcn, ptr ? ptr : "NULL")); 294 295 return (ptr); 296 } 297 298 static char * 299 rskip_bspace(char *bound, char *ptr) 300 { 301 const char *fcn = "rskip_bspace()"; 302 assert(bound); 303 assert(ptr); 304 assert(bound <= ptr); 305 assert(*bound != ' ' && *bound != '\t' && *bound != '\n'); 306 307 BAM_DPRINTF((D_RSKIP_BSPACE_ENTRY, fcn, ptr)); 308 for (; ptr > bound; ptr--) { 309 if (*ptr == ' ' || *ptr == '\t' || *ptr == '\n') 310 break; 311 } 312 313 BAM_DPRINTF((D_RSKIP_BSPACE_EXIT, fcn, ptr)); 314 return (ptr); 315 } 316 317 /* 318 * The parse_kernel_line function examines a menu.lst kernel line. For 319 * multiboot, this is: 320 * 321 * kernel <multiboot path> <flags1> <kernel path> <flags2> 322 * 323 * <multiboot path> is either /platform/i86pc/multiboot or /boot/multiboot 324 * 325 * <kernel path> may be missing, or may be any full or relative path to unix. 326 * We check for it by looking for a word ending in "/unix". If it ends 327 * in "kernel/unix", we upgrade it to a 32-bit entry. If it ends in 328 * "kernel/amd64/unix", we upgrade it to the default entry. Otherwise, 329 * it's a custom kernel, and we skip it. 330 * 331 * <flags*> are anything that doesn't fit either of the above - these will be 332 * copied over. 333 * 334 * For direct boot, the defaults are 335 * 336 * kernel$ <kernel path> <flags> 337 * 338 * <kernel path> is one of: 339 * /platform/i86pc/kernel/$ISADIR/unix 340 * /platform/i86pc/kernel/unix 341 * /platform/i86pc/kernel/amd64/unix 342 * /boot/platform/i86pc/kernel/unix 343 * 344 * If <kernel path> is any of the last three, 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]; 359 char linebuf[PATH_MAX]; 360 char new_arg[PATH_MAX]; 361 struct stat sb; 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_KERNEL); 389 if (stat(path, &sb) != 0) { 390 if (bam_verbose) { 391 bam_error(FAILSAFE_MISSING, line->lineNum); 392 } 393 BAM_DPRINTF((D_NO_FAILSAFE_UNIX_CONVERT, fcn)); 394 return (BAM_SUCCESS); 395 } 396 } 397 398 /* 399 * Make sure we have the correct cmd - either kernel or kernel$ 400 * The failsafe entry should always be kernel. 401 */ 402 if (!(entry->flags & BAM_ENTRY_FAILSAFE)) { 403 free(line->cmd); 404 line->cmd = s_strdup(menu_cmds[KERNEL_DOLLAR_CMD]); 405 BAM_DPRINTF((D_CVT_CMD_KERN_DOLLAR, fcn, line->cmd)); 406 } 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 * For either dboot or multiboot, the failsafe is: 514 * 515 * module /boot/x86.miniroot-safe 516 */ 517 static error_t 518 cvt_module_line(line_t *line, entry_t *entry) 519 { 520 const char *fcn = "cvt_module_line()"; 521 522 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, line->line)); 523 524 /* 525 * We only convert multiboot to dboot and nothing else 526 */ 527 if (!(entry->flags & BAM_ENTRY_MULTIBOOT)) { 528 BAM_DPRINTF((D_NOT_MULTIBOOT_CONVERT, fcn)); 529 return (BAM_SUCCESS); 530 } 531 532 if (entry->flags & BAM_ENTRY_FAILSAFE) { 533 if (strcmp(line->arg, FAILSAFE_ARCHIVE) == 0) { 534 BAM_DPRINTF((D_FAILSAFE_NO_CVT_NEEDED, fcn, line->arg)); 535 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 536 return (BAM_SUCCESS); 537 } 538 } else if (strcmp(line->arg, MULTIBOOT_ARCHIVE) != 0) { 539 bam_error(UNKNOWN_MODULE_LINE, line->lineNum); 540 BAM_DPRINTF((D_RETURN_FAILURE, fcn)); 541 return (BAM_MSG); 542 } 543 544 if (entry->flags & BAM_ENTRY_FAILSAFE) { 545 free(line->cmd); 546 free(line->arg); 547 line->cmd = s_strdup(menu_cmds[MODULE_CMD]); 548 line->arg = s_strdup(FAILSAFE_ARCHIVE); 549 } else { 550 free(line->cmd); 551 free(line->arg); 552 line->cmd = s_strdup(menu_cmds[MODULE_DOLLAR_CMD]); 553 line->arg = s_strdup(DIRECT_BOOT_ARCHIVE); 554 } 555 556 update_line(line); 557 BAM_DPRINTF((D_CVTED_MODULE, fcn, line->line)); 558 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 559 return (BAM_SUCCESS); 560 } 561 562 static void 563 bam_warn_hand_entries(menu_t *mp, char *osroot) 564 { 565 int hand_num; 566 int hand_max; 567 int *hand_list; 568 int i; 569 entry_t *entry; 570 const char *fcn = "bam_warn_hand_entries()"; 571 572 if (bam_force) { 573 /* 574 * No warning needed, we are automatically converting 575 * the "hand" entries 576 */ 577 BAM_DPRINTF((D_FORCE_HAND_CVT, fcn)); 578 return; 579 } 580 581 hand_num = 0; 582 hand_max = BAM_ENTRY_NUM; 583 hand_list = s_calloc(1, hand_max); 584 585 for (entry = mp->entries; entry; entry = entry->next) { 586 if (entry->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU)) 587 continue; 588 BAM_DPRINTF((D_FOUND_HAND, fcn, entry->entryNum)); 589 if (++hand_num > hand_max) { 590 hand_max *= 2; 591 hand_list = s_realloc(hand_list, 592 hand_max * sizeof (int)); 593 } 594 hand_list[hand_num - 1] = entry->entryNum; 595 } 596 597 bam_error(HAND_ADDED_ENTRIES, osroot, MENU_URL(osroot)); 598 bam_print_stderr("Entry Number%s: ", (hand_num > 1) ? 599 "s" : ""); 600 for (i = 0; i < hand_num; i++) { 601 bam_print_stderr("%d ", hand_list[i]); 602 } 603 bam_print_stderr("\n"); 604 } 605 606 static entry_t * 607 find_matching_entry( 608 entry_t *estart, 609 char *grubsign, 610 char *grubroot, 611 int root_opt) 612 { 613 entry_t *entry; 614 line_t *line; 615 char opt[10]; 616 const char *fcn = "find_matching_entry()"; 617 618 assert(grubsign); 619 assert(root_opt == 0 || root_opt == 1); 620 621 (void) snprintf(opt, sizeof (opt), "%d", root_opt); 622 BAM_DPRINTF((D_FUNC_ENTRY3, fcn, grubsign, grubroot, opt)); 623 624 for (entry = estart; entry; entry = entry->next) { 625 626 if (!(entry->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU)) && 627 !bam_force) { 628 BAM_DPRINTF((D_SKIP_ENTRY, fcn, entry->entryNum)); 629 continue; 630 } 631 632 if (entry->flags & BAM_ENTRY_ROOT) { 633 for (line = entry->start; line; line = line->next) { 634 if (line->cmd == NULL || line->arg == NULL) { 635 if (line == entry->end) { 636 BAM_DPRINTF((D_ENTRY_END, fcn)); 637 break; 638 } else { 639 BAM_DPRINTF((D_SKIP_NULL, fcn)); 640 continue; 641 } 642 } 643 if (strcmp(line->cmd, menu_cmds[ROOT_CMD]) 644 == 0 && strcmp(line->arg, grubroot) == 0) { 645 BAM_DPRINTF((D_ROOT_MATCH, fcn, 646 line->line, grubsign)); 647 return (entry); 648 } 649 if (line == entry->end) { 650 BAM_DPRINTF((D_ENTRY_END, fcn)); 651 break; 652 } 653 } 654 } else if (entry->flags & BAM_ENTRY_FINDROOT) { 655 for (line = entry->start; line; line = line->next) { 656 if (line->cmd == NULL || line->arg == NULL) { 657 if (line == entry->end) { 658 BAM_DPRINTF((D_ENTRY_END, fcn)); 659 break; 660 } else { 661 BAM_DPRINTF((D_SKIP_NULL, fcn)); 662 continue; 663 } 664 } 665 if (strcmp(line->cmd, menu_cmds[FINDROOT_CMD]) 666 == 0 && strcmp(line->arg, grubsign) == 0) { 667 BAM_DPRINTF((D_FINDROOT_MATCH, fcn, 668 line->line, grubsign)); 669 return (entry); 670 } 671 if (line == entry->end) { 672 BAM_DPRINTF((D_ENTRY_END, fcn)); 673 break; 674 } 675 } 676 } else if (root_opt) { 677 /* Neither root nor findroot */ 678 BAM_DPRINTF((D_NO_ROOT_FINDROOT, fcn, entry->entryNum)); 679 return (entry); 680 } 681 } 682 683 BAM_DPRINTF((D_NO_MATCH, fcn)); 684 return (NULL); 685 } 686 687 /* 688 * The following is a set of routines that attempt to convert the 689 * menu entries for the supplied osroot into a format compatible 690 * with the GRUB installation on osroot. 691 * 692 * Each of these conversion routines make no assumptions about 693 * the current state of the menu entry, it does its best to 694 * convert the menu entry to the new state. In the process 695 * we may either upgrade or downgrade. 696 * 697 * We don't make any heroic efforts at conversion. It is better 698 * to be conservative and bail out at the first sign of error. We will 699 * in such cases, point the user at the knowledge-base article 700 * so that they can upgrade manually. 701 */ 702 static error_t 703 bam_add_findroot(menu_t *mp, char *grubsign, char *grubroot, int root_opt) 704 { 705 entry_t *entry; 706 line_t *line; 707 line_t *newlp; 708 int update_num; 709 char linebuf[PATH_MAX]; 710 const char *fcn = "bam_add_findroot()"; 711 712 update_num = 0; 713 714 bam_print(CVT_FINDROOT); 715 716 entry = mp->entries; 717 for (; entry = find_matching_entry(entry, grubsign, grubroot, root_opt); 718 entry = entry->next) { 719 if (entry->flags & BAM_ENTRY_FINDROOT) { 720 /* already converted */ 721 BAM_DPRINTF((D_ALREADY_FINDROOT, fcn, entry->entryNum)); 722 continue; 723 } 724 for (line = entry->start; line; line = line->next) { 725 if (line->cmd == NULL || line->arg == NULL) { 726 if (line == entry->end) { 727 BAM_DPRINTF((D_ENTRY_END, fcn)); 728 break; 729 } else { 730 BAM_DPRINTF((D_SKIP_NULL, fcn)); 731 continue; 732 } 733 } 734 if (strcmp(line->cmd, menu_cmds[TITLE_CMD]) == 0) { 735 newlp = s_calloc(1, sizeof (line_t)); 736 newlp->cmd = s_strdup(menu_cmds[FINDROOT_CMD]); 737 newlp->sep = s_strdup(" "); 738 newlp->arg = s_strdup(grubsign); 739 (void) snprintf(linebuf, sizeof (linebuf), 740 "%s%s%s", newlp->cmd, newlp->sep, 741 newlp->arg); 742 newlp->line = s_strdup(linebuf); 743 bam_add_line(mp, entry, line, newlp); 744 update_num = 1; 745 entry->flags &= ~BAM_ENTRY_ROOT; 746 entry->flags |= BAM_ENTRY_FINDROOT; 747 BAM_DPRINTF((D_ADDED_FINDROOT, fcn, 748 newlp->line)); 749 line = newlp; 750 } 751 if (strcmp(line->cmd, menu_cmds[ROOT_CMD]) == 0) { 752 BAM_DPRINTF((D_FREEING_ROOT, fcn, line->line)); 753 unlink_line(mp, line); 754 line_free(line); 755 } 756 if (line == entry->end) { 757 BAM_DPRINTF((D_ENTRY_END, fcn)); 758 break; 759 } 760 } 761 } 762 763 if (update_num) { 764 BAM_DPRINTF((D_UPDATED_NUMBERING, fcn)); 765 update_numbering(mp); 766 } 767 768 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 769 return (BAM_SUCCESS); 770 } 771 772 static error_t 773 bam_add_hv(menu_t *mp, char *grubsign, char *grubroot, int root_opt) 774 { 775 entry_t *entry; 776 const char *fcn = "bam_add_hv()"; 777 778 bam_print(CVT_HV); 779 780 entry = mp->entries; 781 for (; entry = find_matching_entry(entry, grubsign, grubroot, root_opt); 782 entry = entry->next) { 783 if (entry->flags & BAM_ENTRY_HV) { 784 BAM_DPRINTF((D_ALREADY_HV, fcn, entry->entryNum)); 785 return (BAM_SUCCESS); 786 } 787 } 788 789 (void) add_boot_entry(mp, NEW_HV_ENTRY, grubsign, XEN_MENU, 790 XEN_KERNEL_MODULE_LINE, DIRECT_BOOT_ARCHIVE); 791 792 BAM_DPRINTF((D_ADDED_XVM_ENTRY, fcn)); 793 794 update_numbering(mp); 795 796 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 797 798 return (BAM_SUCCESS); 799 } 800 801 static error_t 802 bam_add_dboot( 803 menu_t *mp, 804 char *osroot, 805 char *grubsign, 806 char *grubroot, 807 int root_opt) 808 { 809 int msg = 0; 810 entry_t *entry; 811 line_t *line; 812 error_t ret; 813 const char *fcn = "bam_add_dboot()"; 814 815 bam_print(CVT_DBOOT); 816 817 entry = mp->entries; 818 for (; entry = find_matching_entry(entry, grubsign, grubroot, root_opt); 819 entry = entry->next) { 820 for (line = entry->start; line; line = line->next) { 821 if (line->cmd == NULL || line->arg == NULL) { 822 if (line == entry->end) { 823 BAM_DPRINTF((D_ENTRY_END, fcn)); 824 break; 825 } else { 826 BAM_DPRINTF((D_SKIP_NULL, fcn)); 827 continue; 828 } 829 } 830 831 /* 832 * If we have a kernel$ command, assume it 833 * is dboot already. If it is not a dboot 834 * entry, something funny is going on and 835 * we will leave it alone 836 */ 837 if (strcmp(line->cmd, menu_cmds[KERNEL_CMD]) == 0) { 838 ret = cvt_kernel_line(line, osroot, entry); 839 INJECT_ERROR1("ADD_DBOOT_KERN_ERR", 840 ret = BAM_ERROR); 841 INJECT_ERROR1("ADD_DBOOT_KERN_MSG", 842 ret = BAM_MSG); 843 if (ret == BAM_ERROR) { 844 BAM_DPRINTF((D_CVT_KERNEL_FAIL, fcn)); 845 return (ret); 846 } else if (ret == BAM_MSG) { 847 msg = 1; 848 BAM_DPRINTF((D_CVT_KERNEL_MSG, fcn)); 849 } 850 } 851 if (strcmp(line->cmd, menu_cmds[MODULE_CMD]) == 0) { 852 ret = cvt_module_line(line, entry); 853 INJECT_ERROR1("ADD_DBOOT_MOD_ERR", 854 ret = BAM_ERROR); 855 INJECT_ERROR1("ADD_DBOOT_MOD_MSG", 856 ret = BAM_MSG); 857 if (ret == BAM_ERROR) { 858 BAM_DPRINTF((D_CVT_MODULE_FAIL, fcn)); 859 return (ret); 860 } else if (ret == BAM_MSG) { 861 BAM_DPRINTF((D_CVT_MODULE_MSG, fcn)); 862 msg = 1; 863 } 864 } 865 866 if (line == entry->end) { 867 BAM_DPRINTF((D_ENTRY_END, fcn)); 868 break; 869 } 870 } 871 } 872 873 ret = msg ? BAM_MSG : BAM_SUCCESS; 874 BAM_DPRINTF((D_RETURN_RET, fcn, ret)); 875 return (ret); 876 } 877 878 /*ARGSUSED*/ 879 error_t 880 upgrade_menu(menu_t *mp, char *osroot, char *menu_root) 881 { 882 char *osdev; 883 char *grubsign; 884 char *grubroot; 885 int ret1; 886 int ret2; 887 int ret3; 888 const char *fcn = "upgrade_menu()"; 889 890 assert(osroot); 891 assert(menu_root); 892 893 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, menu_root)); 894 895 /* 896 * We only support upgrades. Xen may not be present 897 * on smaller metaclusters so we don't check for that. 898 */ 899 if (bam_is_findroot != BAM_FINDROOT_PRESENT || 900 bam_direct != BAM_DIRECT_DBOOT) { 901 bam_error(DOWNGRADE_NOTSUP, osroot); 902 return (BAM_ERROR); 903 } 904 905 /* 906 * First get the GRUB signature 907 */ 908 osdev = get_special(osroot); 909 INJECT_ERROR1("UPGRADE_OSDEV", osdev = NULL); 910 if (osdev == NULL) { 911 bam_error(CANT_FIND_SPECIAL, osroot); 912 return (BAM_ERROR); 913 } 914 915 grubsign = get_grubsign(osroot, osdev); 916 INJECT_ERROR1("UPGRADE_GRUBSIGN", grubsign = NULL); 917 if (grubsign == NULL) { 918 free(osdev); 919 bam_error(CANT_FIND_GRUBSIGN, osroot); 920 return (BAM_ERROR); 921 } 922 923 /* not fatal if we can't get grubroot */ 924 grubroot = get_grubroot(osroot, osdev, menu_root); 925 INJECT_ERROR1("UPGRADE_GRUBROOT", grubroot = NULL); 926 927 free(osdev); 928 929 ret1 = bam_add_findroot(mp, grubsign, 930 grubroot, root_optional(osroot, menu_root)); 931 INJECT_ERROR1("UPGRADE_ADD_FINDROOT", ret1 = BAM_ERROR); 932 if (ret1 == BAM_ERROR) 933 goto abort; 934 935 ret2 = bam_add_hv(mp, grubsign, grubroot, 936 root_optional(osroot, menu_root)); 937 INJECT_ERROR1("UPGRADE_ADD_HV", ret2 = BAM_ERROR); 938 if (ret2 == BAM_ERROR) 939 goto abort; 940 941 ret3 = bam_add_dboot(mp, osroot, grubsign, 942 grubroot, root_optional(osroot, menu_root)); 943 INJECT_ERROR1("UPGRADE_ADD_DBOOT", ret3 = BAM_ERROR); 944 if (ret3 == BAM_ERROR) 945 goto abort; 946 947 if (ret1 == BAM_MSG || ret2 == BAM_MSG || ret3 == BAM_MSG) { 948 bam_error(CVT_TODO, MENU_URL(osroot)); 949 } else { 950 bam_warn_hand_entries(mp, osroot); 951 } 952 953 free(grubsign); 954 955 BAM_DPRINTF((D_RETURN_RET, fcn, BAM_WRITE)); 956 return (BAM_WRITE); 957 958 abort: 959 free(grubsign); 960 bam_error(CVT_ABORT, osroot, MENU_URL(osroot)); 961 return (BAM_ERROR); 962 } 963