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 #if 0 626 if (!(entry->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU)) && 627 !bam_force) { 628 #endif 629 if (!(entry->flags & BAM_ENTRY_BOOTADM) && !bam_force) { 630 BAM_DPRINTF((D_SKIP_ENTRY, fcn, entry->entryNum)); 631 continue; 632 } 633 634 if (entry->flags & BAM_ENTRY_ROOT) { 635 for (line = entry->start; line; line = line->next) { 636 if (line->cmd == NULL || line->arg == NULL) { 637 if (line == entry->end) { 638 BAM_DPRINTF((D_ENTRY_END, fcn)); 639 break; 640 } else { 641 BAM_DPRINTF((D_SKIP_NULL, fcn)); 642 continue; 643 } 644 } 645 if (strcmp(line->cmd, menu_cmds[ROOT_CMD]) 646 == 0 && strcmp(line->arg, grubroot) == 0) { 647 BAM_DPRINTF((D_ROOT_MATCH, fcn, 648 line->line, grubsign)); 649 return (entry); 650 } 651 if (line == entry->end) { 652 BAM_DPRINTF((D_ENTRY_END, fcn)); 653 break; 654 } 655 } 656 } else if (entry->flags & BAM_ENTRY_FINDROOT) { 657 for (line = entry->start; line; line = line->next) { 658 if (line->cmd == NULL || line->arg == NULL) { 659 if (line == entry->end) { 660 BAM_DPRINTF((D_ENTRY_END, fcn)); 661 break; 662 } else { 663 BAM_DPRINTF((D_SKIP_NULL, fcn)); 664 continue; 665 } 666 } 667 if (strcmp(line->cmd, menu_cmds[FINDROOT_CMD]) 668 == 0 && strcmp(line->arg, grubsign) == 0) { 669 BAM_DPRINTF((D_FINDROOT_MATCH, fcn, 670 line->line, grubsign)); 671 return (entry); 672 } 673 if (line == entry->end) { 674 BAM_DPRINTF((D_ENTRY_END, fcn)); 675 break; 676 } 677 } 678 } else if (root_opt) { 679 /* Neither root nor findroot */ 680 BAM_DPRINTF((D_NO_ROOT_FINDROOT, fcn, entry->entryNum)); 681 return (entry); 682 } 683 } 684 685 BAM_DPRINTF((D_NO_MATCH, fcn)); 686 return (NULL); 687 } 688 689 /* 690 * The following is a set of routines that attempt to convert the 691 * menu entries for the supplied osroot into a format compatible 692 * with the GRUB installation on osroot. 693 * 694 * Each of these conversion routines make no assumptions about 695 * the current state of the menu entry, it does its best to 696 * convert the menu entry to the new state. In the process 697 * we may either upgrade or downgrade. 698 * 699 * We don't make any heroic efforts at conversion. It is better 700 * to be conservative and bail out at the first sign of error. We will 701 * in such cases, point the user at the knowledge-base article 702 * so that they can upgrade manually. 703 */ 704 static error_t 705 bam_add_findroot(menu_t *mp, char *grubsign, char *grubroot, int root_opt) 706 { 707 entry_t *entry; 708 line_t *line; 709 line_t *newlp; 710 int update_num; 711 char linebuf[PATH_MAX]; 712 const char *fcn = "bam_add_findroot()"; 713 714 update_num = 0; 715 716 bam_print(CVT_FINDROOT); 717 718 entry = mp->entries; 719 for (; entry = find_matching_entry(entry, grubsign, grubroot, root_opt); 720 entry = entry->next) { 721 if (entry->flags & BAM_ENTRY_FINDROOT) { 722 /* already converted */ 723 BAM_DPRINTF((D_ALREADY_FINDROOT, fcn, entry->entryNum)); 724 continue; 725 } 726 for (line = entry->start; line; line = line->next) { 727 if (line->cmd == NULL || line->arg == NULL) { 728 if (line == entry->end) { 729 BAM_DPRINTF((D_ENTRY_END, fcn)); 730 break; 731 } else { 732 BAM_DPRINTF((D_SKIP_NULL, fcn)); 733 continue; 734 } 735 } 736 if (strcmp(line->cmd, menu_cmds[TITLE_CMD]) == 0) { 737 newlp = s_calloc(1, sizeof (line_t)); 738 newlp->cmd = s_strdup(menu_cmds[FINDROOT_CMD]); 739 newlp->sep = s_strdup(" "); 740 newlp->arg = s_strdup(grubsign); 741 (void) snprintf(linebuf, sizeof (linebuf), 742 "%s%s%s", newlp->cmd, newlp->sep, 743 newlp->arg); 744 newlp->line = s_strdup(linebuf); 745 bam_add_line(mp, entry, line, newlp); 746 update_num = 1; 747 entry->flags &= ~BAM_ENTRY_ROOT; 748 entry->flags |= BAM_ENTRY_FINDROOT; 749 BAM_DPRINTF((D_ADDED_FINDROOT, fcn, 750 newlp->line)); 751 line = newlp; 752 } 753 if (strcmp(line->cmd, menu_cmds[ROOT_CMD]) == 0) { 754 BAM_DPRINTF((D_FREEING_ROOT, fcn, line->line)); 755 unlink_line(mp, line); 756 line_free(line); 757 } 758 if (line == entry->end) { 759 BAM_DPRINTF((D_ENTRY_END, fcn)); 760 break; 761 } 762 } 763 } 764 765 if (update_num) { 766 BAM_DPRINTF((D_UPDATED_NUMBERING, fcn)); 767 update_numbering(mp); 768 } 769 770 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 771 return (BAM_SUCCESS); 772 } 773 774 static error_t 775 bam_add_hv(menu_t *mp, char *grubsign, char *grubroot, int root_opt) 776 { 777 entry_t *entry; 778 const char *fcn = "bam_add_hv()"; 779 780 bam_print(CVT_HV); 781 782 entry = mp->entries; 783 for (; entry = find_matching_entry(entry, grubsign, grubroot, root_opt); 784 entry = entry->next) { 785 if (entry->flags & BAM_ENTRY_HV) { 786 BAM_DPRINTF((D_ALREADY_HV, fcn, entry->entryNum)); 787 return (BAM_SUCCESS); 788 } 789 } 790 791 (void) add_boot_entry(mp, NEW_HV_ENTRY, grubsign, XEN_MENU, 792 XEN_KERNEL_MODULE_LINE, DIRECT_BOOT_ARCHIVE); 793 794 BAM_DPRINTF((D_ADDED_XVM_ENTRY, fcn)); 795 796 update_numbering(mp); 797 798 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 799 800 return (BAM_SUCCESS); 801 } 802 803 static error_t 804 bam_add_dboot( 805 menu_t *mp, 806 char *osroot, 807 char *grubsign, 808 char *grubroot, 809 int root_opt) 810 { 811 int msg = 0; 812 entry_t *entry; 813 line_t *line; 814 error_t ret; 815 const char *fcn = "bam_add_dboot()"; 816 817 bam_print(CVT_DBOOT); 818 819 entry = mp->entries; 820 for (; entry = find_matching_entry(entry, grubsign, grubroot, root_opt); 821 entry = entry->next) { 822 for (line = entry->start; line; line = line->next) { 823 if (line->cmd == NULL || line->arg == NULL) { 824 if (line == entry->end) { 825 BAM_DPRINTF((D_ENTRY_END, fcn)); 826 break; 827 } else { 828 BAM_DPRINTF((D_SKIP_NULL, fcn)); 829 continue; 830 } 831 } 832 833 /* 834 * If we have a kernel$ command, assume it 835 * is dboot already. If it is not a dboot 836 * entry, something funny is going on and 837 * we will leave it alone 838 */ 839 if (strcmp(line->cmd, menu_cmds[KERNEL_CMD]) == 0) { 840 ret = cvt_kernel_line(line, osroot, entry); 841 INJECT_ERROR1("ADD_DBOOT_KERN_ERR", 842 ret = BAM_ERROR); 843 INJECT_ERROR1("ADD_DBOOT_KERN_MSG", 844 ret = BAM_MSG); 845 if (ret == BAM_ERROR) { 846 BAM_DPRINTF((D_CVT_KERNEL_FAIL, fcn)); 847 return (ret); 848 } else if (ret == BAM_MSG) { 849 msg = 1; 850 BAM_DPRINTF((D_CVT_KERNEL_MSG, fcn)); 851 } 852 } 853 if (strcmp(line->cmd, menu_cmds[MODULE_CMD]) == 0) { 854 ret = cvt_module_line(line, entry); 855 INJECT_ERROR1("ADD_DBOOT_MOD_ERR", 856 ret = BAM_ERROR); 857 INJECT_ERROR1("ADD_DBOOT_MOD_MSG", 858 ret = BAM_MSG); 859 if (ret == BAM_ERROR) { 860 BAM_DPRINTF((D_CVT_MODULE_FAIL, fcn)); 861 return (ret); 862 } else if (ret == BAM_MSG) { 863 BAM_DPRINTF((D_CVT_MODULE_MSG, fcn)); 864 msg = 1; 865 } 866 } 867 868 if (line == entry->end) { 869 BAM_DPRINTF((D_ENTRY_END, fcn)); 870 break; 871 } 872 } 873 } 874 875 ret = msg ? BAM_MSG : BAM_SUCCESS; 876 BAM_DPRINTF((D_RETURN_RET, fcn, ret)); 877 return (ret); 878 } 879 880 /*ARGSUSED*/ 881 error_t 882 upgrade_menu(menu_t *mp, char *osroot, char *menu_root) 883 { 884 char *osdev; 885 char *grubsign; 886 char *grubroot; 887 int ret1; 888 int ret2; 889 int ret3; 890 const char *fcn = "upgrade_menu()"; 891 892 assert(osroot); 893 assert(menu_root); 894 895 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, menu_root)); 896 897 /* 898 * We only support upgrades. Xen may not be present 899 * on smaller metaclusters so we don't check for that. 900 */ 901 if (bam_is_findroot != BAM_FINDROOT_PRESENT || 902 bam_direct != BAM_DIRECT_DBOOT) { 903 bam_error(DOWNGRADE_NOTSUP, osroot); 904 return (BAM_ERROR); 905 } 906 907 /* 908 * First get the GRUB signature 909 */ 910 osdev = get_special(osroot); 911 INJECT_ERROR1("UPGRADE_OSDEV", osdev = NULL); 912 if (osdev == NULL) { 913 bam_error(CANT_FIND_SPECIAL, osroot); 914 return (BAM_ERROR); 915 } 916 917 grubsign = get_grubsign(osroot, osdev); 918 INJECT_ERROR1("UPGRADE_GRUBSIGN", grubsign = NULL); 919 if (grubsign == NULL) { 920 free(osdev); 921 bam_error(CANT_FIND_GRUBSIGN, osroot); 922 return (BAM_ERROR); 923 } 924 925 /* not fatal if we can't get grubroot */ 926 grubroot = get_grubroot(osroot, osdev, menu_root); 927 INJECT_ERROR1("UPGRADE_GRUBROOT", grubroot = NULL); 928 929 free(osdev); 930 931 ret1 = bam_add_findroot(mp, grubsign, 932 grubroot, root_optional(osroot, menu_root)); 933 INJECT_ERROR1("UPGRADE_ADD_FINDROOT", ret1 = BAM_ERROR); 934 if (ret1 == BAM_ERROR) 935 goto abort; 936 937 ret2 = bam_add_hv(mp, grubsign, grubroot, 938 root_optional(osroot, menu_root)); 939 INJECT_ERROR1("UPGRADE_ADD_HV", ret2 = BAM_ERROR); 940 if (ret2 == BAM_ERROR) 941 goto abort; 942 943 ret3 = bam_add_dboot(mp, osroot, grubsign, 944 grubroot, root_optional(osroot, menu_root)); 945 INJECT_ERROR1("UPGRADE_ADD_DBOOT", ret3 = BAM_ERROR); 946 if (ret3 == BAM_ERROR) 947 goto abort; 948 949 if (ret1 == BAM_MSG || ret2 == BAM_MSG || ret3 == BAM_MSG) { 950 bam_error(CVT_TODO, MENU_URL(osroot)); 951 } else { 952 bam_warn_hand_entries(mp, osroot); 953 } 954 955 free(grubsign); 956 957 BAM_DPRINTF((D_RETURN_RET, fcn, BAM_WRITE)); 958 return (BAM_WRITE); 959 960 abort: 961 free(grubsign); 962 bam_error(CVT_ABORT, osroot, MENU_URL(osroot)); 963 return (BAM_ERROR); 964 } 965