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