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