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 (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
  23  * Copyright 2012 Milan Jurik. All rights reserved.
  24  */
  25 
  26 /*
  27  * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
  28  * Copyright 2016 Toomas Soome <tsoome@me.com>
  29  */
  30 
  31 /*
  32  * Loader menu management.
  33  */
  34 
  35 #include <stdio.h>
  36 #include <stdlib.h>
  37 #include <string.h>
  38 #include <wchar.h>
  39 #include <errno.h>
  40 #include <limits.h>
  41 #include <alloca.h>
  42 #include <unistd.h>
  43 #include <sys/types.h>
  44 #include <sys/stat.h>
  45 #include <sys/queue.h>
  46 #include <libbe.h>
  47 #include <ficl.h>
  48 #include <ficlplatform/emu.h>
  49 #include <ofmt.h>
  50 
  51 #include "bootadm.h"
  52 
  53 extern int bam_rootlen;
  54 extern int bam_alt_root;
  55 extern char *rootbuf;
  56 extern char *bam_root;
  57 
  58 #define BOOT_DIR        "/boot"
  59 #define CONF_DIR        BOOT_DIR "/conf.d"
  60 #define MENU            BOOT_DIR "/menu.lst"
  61 #define TRANSIENT       BOOT_DIR "/transient.conf"
  62 #define XEN_CONFIG      CONF_DIR "/xen"
  63 
  64 typedef struct menu_entry {
  65         int me_idx;
  66         boolean_t me_active;
  67         char *me_title;
  68         char *me_type;
  69         char *me_bootfs;
  70         STAILQ_ENTRY(menu_entry) me_next;
  71 } menu_entry_t;
  72 STAILQ_HEAD(menu_lst, menu_entry);
  73 
  74 static error_t set_option(struct menu_lst *, char *, char *);
  75 static error_t list_entry(struct menu_lst *, char *, char *);
  76 static error_t update_entry(struct menu_lst *, char *, char *);
  77 static error_t update_temp(struct menu_lst *, char *, char *);
  78 static error_t list_setting(struct menu_lst *menu, char *, char *);
  79 static error_t disable_hyper(struct menu_lst *, char *, char *);
  80 static error_t enable_hyper(struct menu_lst *, char *, char *);
  81 
  82 /* Menu related sub commands */
  83 static subcmd_defn_t menu_subcmds[] = {
  84         "set_option",           OPT_ABSENT,     set_option, 0,  /* PUB */
  85         "list_entry",           OPT_OPTIONAL,   list_entry, 1,  /* PUB */
  86         "update_entry",         OPT_REQ,        update_entry, 0, /* menu */
  87         "update_temp",          OPT_OPTIONAL,   update_temp, 0, /* reboot */
  88         "list_setting",         OPT_OPTIONAL,   list_setting, 1, /* menu */
  89         "disable_hypervisor",   OPT_ABSENT,     disable_hyper, 0, /* menu */
  90         "enable_hypervisor",    OPT_ABSENT,     enable_hyper, 0, /* menu */
  91         NULL,                   0,              NULL, 0 /* must be last */
  92 };
  93 
  94 #define NUM_COLS        (5)
  95 
  96 static boolean_t
  97 print_menu_cb(ofmt_arg_t *ofarg, char *buf, uint_t bufsize)
  98 {
  99         menu_entry_t *entry = ofarg->ofmt_cbarg;
 100 
 101         switch (ofarg->ofmt_id) {
 102         case 0:
 103                 (void) snprintf(buf, bufsize, "%d", entry->me_idx);
 104                 break;
 105         case 1:
 106                 (void) snprintf(buf, bufsize, "%s", entry->me_title);
 107                 break;
 108         case 2:
 109                 (void) snprintf(buf, bufsize, "%s", entry->me_bootfs);
 110                 break;
 111         case 3:
 112                 (void) snprintf(buf, bufsize, "%s", entry->me_type);
 113                 break;
 114         case 4:
 115                 if (entry->me_active == B_TRUE)
 116                         (void) snprintf(buf, bufsize, "   *");
 117                 else
 118                         (void) snprintf(buf, bufsize, "   -");
 119                 break;
 120         default:
 121                 return (B_FALSE);
 122         }
 123         return (B_TRUE);
 124 }
 125 
 126 static void
 127 init_hdr_cols(ofmt_field_t *hdr)
 128 {
 129         uint_t i;
 130 
 131         for (i = 0; i < NUM_COLS; i++) {
 132                 char *name = NULL;
 133 
 134                 switch (i) {
 135                 case 0:
 136                         name = _("INDEX");
 137                         break;
 138                 case 1:
 139                         name = _("NAME");
 140                         break;
 141                 case 2:
 142                         name = _("DEVICE");
 143                         break;
 144                 case 3:
 145                         name = _("TYPE");
 146                         break;
 147                 case 4:
 148                         name = _("DEFAULT");
 149                         break;
 150                 }
 151 
 152                 hdr[i].of_name = name;
 153                 hdr[i].of_id = i;
 154                 hdr[i].of_cb = print_menu_cb;
 155 
 156                 if (name != NULL) {
 157                         wchar_t wname[128];
 158                         size_t sz = mbstowcs(wname, name, sizeof (wname) /
 159                             sizeof (wchar_t));
 160                         if (sz > 0) {
 161                                 int wcsw = wcswidth(wname, sz);
 162                                 if (wcsw > 0)
 163                                         hdr[i].of_width = wcsw;
 164                                 else
 165                                         hdr[i].of_width = sz;
 166                         } else {
 167                                 hdr[i].of_width = strlen(name);
 168                         }
 169                 }
 170         }
 171 }
 172 
 173 static void
 174 menu_update_widths(ofmt_field_t *hdr, struct menu_lst *menu)
 175 {
 176         size_t len[NUM_COLS];
 177         menu_entry_t *entry;
 178         int i;
 179 
 180         for (i = 0; i < NUM_COLS; i++)
 181                 len[i] = hdr[i].of_width + 1;
 182 
 183         STAILQ_FOREACH(entry, menu, me_next) {
 184                 size_t entry_len;
 185 
 186                 entry_len = strlen(entry->me_title) + 1;
 187                 if (entry_len > len[1])
 188                         len[1] = entry_len;
 189 
 190                 entry_len = strlen(entry->me_bootfs) + 1;
 191                 if (entry_len > len[2])
 192                         len[2] = entry_len;
 193 
 194                 entry_len = strlen(entry->me_type) + 1;
 195                 if (entry_len > len[3])
 196                         len[3] = entry_len;
 197         }
 198 
 199         for (i = 0; i < NUM_COLS; i++)
 200                 hdr[i].of_width = len[i];
 201 }
 202 
 203 static ofmt_field_t *
 204 init_menu_template(struct menu_lst *menu)
 205 {
 206         ofmt_field_t *temp;
 207 
 208         if ((temp = calloc(NUM_COLS + 1, sizeof (ofmt_field_t))) == NULL)
 209                 return (temp);
 210 
 211         init_hdr_cols(temp);
 212         menu_update_widths(temp, menu);
 213         return (temp);
 214 }
 215 
 216 static void
 217 print_nodes(boolean_t parsable, struct menu_lst *menu)
 218 {
 219         ofmt_status_t oferr;
 220         ofmt_handle_t ofmt;
 221         uint_t ofmtflags = 0;
 222         ofmt_field_t *menu_template;
 223         menu_entry_t  *entry;
 224 
 225         if (parsable == B_TRUE)
 226                 ofmtflags = OFMT_PARSABLE;
 227 
 228         menu_template = init_menu_template(menu);
 229         oferr = ofmt_open(NULL, menu_template, ofmtflags, 0, &ofmt);
 230 
 231         if (oferr != OFMT_SUCCESS) {
 232                 char buf[OFMT_BUFSIZE];
 233 
 234                 (void) ofmt_strerror(ofmt, oferr, buf, sizeof (buf));
 235                 (void) printf("bootadm: %s\n", buf);
 236                 free(menu_template);
 237                 return;
 238         }
 239 
 240         STAILQ_FOREACH(entry, menu, me_next)
 241                 ofmt_print(ofmt, entry);
 242 
 243         ofmt_close(ofmt);
 244         free(menu_template);
 245 }
 246 
 247 /*
 248  * Get the be_active_on_boot for bootfs.
 249  */
 250 static boolean_t
 251 menu_active_on_boot(be_node_list_t *be_nodes, const char *bootfs)
 252 {
 253         be_node_list_t *be_node;
 254         boolean_t rv = B_FALSE;
 255 
 256         for (be_node = be_nodes; be_node != NULL;
 257             be_node = be_node->be_next_node) {
 258                 if (strcmp(be_node->be_root_ds, bootfs) == 0) {
 259                         rv = be_node->be_active_on_boot;
 260                         break;
 261                 }
 262         }
 263 
 264         return (rv);
 265 }
 266 
 267 error_t
 268 menu_read(struct menu_lst *menu, char *menu_path)
 269 {
 270         FILE *fp;
 271         be_node_list_t *be_nodes;
 272         menu_entry_t *mp;
 273         char buf[PATH_MAX];
 274         char *title;
 275         char *bootfs;
 276         char *type;
 277         char *key, *value;
 278         int i = 0;
 279         int ret = BAM_SUCCESS;
 280 
 281         fp = fopen(menu_path, "r");
 282         if (fp == NULL)
 283                 return (BAM_ERROR);
 284 
 285         if (be_list(NULL, &be_nodes) != BE_SUCCESS)
 286                 be_nodes = NULL;
 287 
 288         /*
 289          * menu.lst entry is on two lines, one for title, one for bootfs
 290          * so we process both lines in succession.
 291          */
 292         title = NULL;
 293         type = NULL;
 294         bootfs = NULL;
 295         do {
 296                 if (fgets(buf, PATH_MAX, fp) == NULL) {
 297                         if (!feof(fp))
 298                                 ret = BAM_ERROR;
 299                         goto done;
 300                 }
 301                 key = strtok(buf, " \n");
 302                 if (strcmp(key, "title") != 0) {
 303                         ret = BAM_ERROR;
 304                         goto done;
 305                 }
 306                 value = strtok(NULL, " \n");
 307                 if ((title = strdup(value)) == NULL) {
 308                         ret = BAM_ERROR;
 309                         goto done;
 310                 }
 311 
 312                 if (fgets(buf, PATH_MAX, fp) == NULL) {
 313                         ret = BAM_ERROR;
 314                         goto done;
 315                 }
 316 
 317                 key = strtok(buf, " \n");
 318                 if ((type = strdup(key)) == NULL) {
 319                         ret = BAM_ERROR;
 320                         goto done;
 321                 }
 322                 value = strtok(NULL, " \n");
 323                 if ((bootfs = strdup(value)) == NULL) {
 324                         ret = BAM_ERROR;
 325                         goto done;
 326                 }
 327                 if ((mp = malloc(sizeof (menu_entry_t))) == NULL) {
 328                         ret = BAM_ERROR;
 329                         goto done;
 330                 }
 331                 mp->me_idx = i++;
 332                 mp->me_title = title;
 333                 mp->me_type = type;
 334                 mp->me_bootfs = bootfs;
 335                 mp->me_active = menu_active_on_boot(be_nodes, bootfs);
 336                 STAILQ_INSERT_TAIL(menu, mp, me_next);
 337 
 338                 title = NULL;
 339                 type = NULL;
 340                 bootfs = NULL;
 341         } while (feof(fp) == 0);
 342 
 343 done:
 344         free(title);
 345         free(type);
 346         free(bootfs);
 347         (void) fclose(fp);
 348         be_free_list(be_nodes);
 349         return (ret);
 350 }
 351 
 352 void
 353 menu_free(struct menu_lst *menu)
 354 {
 355         menu_entry_t *entry;
 356         STAILQ_FOREACH(entry, menu, me_next) {
 357                 STAILQ_REMOVE_HEAD(menu, me_next);
 358                 free(entry->me_title);
 359                 free(entry->me_type);
 360                 free(entry->me_bootfs);
 361                 free(entry);
 362         }
 363 }
 364 
 365 error_t
 366 bam_loader_menu(char *subcmd, char *opt, int largc, char *largv[])
 367 {
 368         error_t         ret;
 369         char            menu_path[PATH_MAX];
 370         char            clean_menu_root[PATH_MAX];
 371         char            menu_root[PATH_MAX];
 372         struct stat     sb;
 373         error_t         (*f)(struct menu_lst *, char *, char *);
 374         char            *special;
 375         char            *pool = NULL;
 376         zfs_mnted_t     zmnted;
 377         char            *zmntpt;
 378         char            *osdev;
 379         char            *osroot;
 380         const char      *fcn = "bam_loader_menu()";
 381         struct menu_lst menu = {0};
 382 
 383         STAILQ_INIT(&menu);
 384 
 385         /*
 386          * Check arguments
 387          */
 388         ret = check_subcmd_and_options(subcmd, opt, menu_subcmds, &f);
 389         if (ret == BAM_ERROR) {
 390                 return (BAM_ERROR);
 391         }
 392 
 393         assert(bam_root);
 394 
 395         (void) strlcpy(menu_root, bam_root, sizeof (menu_root));
 396         osdev = osroot = NULL;
 397 
 398         if (strcmp(subcmd, "update_entry") == 0) {
 399                 assert(opt);
 400 
 401                 osdev = strtok(opt, ",");
 402                 assert(osdev);
 403                 osroot = strtok(NULL, ",");
 404                 if (osroot) {
 405                         /* fixup bam_root so that it points at osroot */
 406                         if (realpath(osroot, rootbuf) == NULL) {
 407                                 bam_error(_("cannot resolve path %s: %s\n"),
 408                                     osroot, strerror(errno));
 409                                 return (BAM_ERROR);
 410                         }
 411                         bam_alt_root = 1;
 412                         bam_root  = rootbuf;
 413                         bam_rootlen = strlen(rootbuf);
 414                 }
 415         }
 416 
 417         if (stat(menu_root, &sb) == -1) {
 418                 bam_error(_("cannot find menu\n"));
 419                 return (BAM_ERROR);
 420         }
 421 
 422         if (!is_zfs(menu_root)) {
 423                 bam_error(_("only ZFS root is supported\n"));
 424                 return (BAM_ERROR);
 425         }
 426 
 427         assert(strcmp(menu_root, bam_root) == 0);
 428         special = get_special(menu_root);
 429         INJECT_ERROR1("Z_MENU_GET_SPECIAL", special = NULL);
 430         if (special == NULL) {
 431                 bam_error(_("cant find special file for mount-point %s\n"),
 432                     menu_root);
 433                 return (BAM_ERROR);
 434         }
 435         pool = strtok(special, "/");
 436         INJECT_ERROR1("Z_MENU_GET_POOL", pool = NULL);
 437         if (pool == NULL) {
 438                 free(special);
 439                 bam_error(_("cant find pool for mount-point %s\n"), menu_root);
 440                 return (BAM_ERROR);
 441         }
 442         BAM_DPRINTF(("%s: derived pool=%s from special\n", fcn, pool));
 443 
 444         zmntpt = mount_top_dataset(pool, &zmnted);
 445         INJECT_ERROR1("Z_MENU_MOUNT_TOP_DATASET", zmntpt = NULL);
 446         if (zmntpt == NULL) {
 447                 bam_error(_("cannot mount pool dataset for pool: %s\n"), pool);
 448                 free(special);
 449                 return (BAM_ERROR);
 450         }
 451         BAM_DPRINTF(("%s: top dataset mountpoint=%s\n", fcn, zmntpt));
 452 
 453         (void) strlcpy(menu_root, zmntpt, sizeof (menu_root));
 454         BAM_DPRINTF(("%s: zfs menu_root=%s\n", fcn, menu_root));
 455 
 456         elide_trailing_slash(menu_root, clean_menu_root,
 457             sizeof (clean_menu_root));
 458 
 459         BAM_DPRINTF(("%s: cleaned menu root is <%s>\n", fcn, clean_menu_root));
 460 
 461         (void) strlcpy(menu_path, clean_menu_root, sizeof (menu_path));
 462         (void) strlcat(menu_path, MENU, sizeof (menu_path));
 463 
 464         BAM_DPRINTF(("%s: menu path is: %s\n", fcn, menu_path));
 465 
 466         /*
 467          * update_entry is special case, its used by installer
 468          * and needs to create menu.lst file for loader
 469          */
 470         if (menu_read(&menu, menu_path) == BAM_ERROR &&
 471             strcmp(subcmd, "update_entry") != 0) {
 472                 bam_error(_("cannot find menu file: %s\n"), menu_path);
 473                 if (special != NULL)
 474                         free(special);
 475                 return (BAM_ERROR);
 476         }
 477 
 478         /*
 479          * If listing the menu, display the menu location
 480          */
 481         if (strcmp(subcmd, "list_entry") == 0)
 482                 bam_print(_("the location for the active menu is: %s\n"),
 483                     menu_path);
 484 
 485         /*
 486          * We already checked the following case in
 487          * check_subcmd_and_suboptions() above. Complete the
 488          * final step now.
 489          */
 490         if (strcmp(subcmd, "set_option") == 0) {
 491                 assert(largc == 1 && largv[0] && largv[1] == NULL);
 492                 opt = largv[0];
 493         } else if ((strcmp(subcmd, "enable_hypervisor") != 0) &&
 494             (strcmp(subcmd, "list_setting") != 0)) {
 495                 assert(largc == 0 && largv == NULL);
 496         }
 497 
 498         /*
 499          * Once the sub-cmd handler has run
 500          * only the line field is guaranteed to have valid values
 501          */
 502         if (strcmp(subcmd, "update_entry") == 0) {
 503                 ret = f(&menu, menu_root, osdev);
 504         } else if (strcmp(subcmd, "upgrade") == 0) {
 505                 ret = f(&menu, bam_root, menu_root);
 506         } else if (strcmp(subcmd, "list_entry") == 0) {
 507                 ret = f(&menu, menu_path, opt);
 508         } else if (strcmp(subcmd, "list_setting") == 0) {
 509                 ret = f(&menu, ((largc > 0) ? largv[0] : ""),
 510                     ((largc > 1) ? largv[1] : ""));
 511         } else if (strcmp(subcmd, "disable_hypervisor") == 0) {
 512                 if (is_sparc()) {
 513                         bam_error(_("%s operation unsupported on SPARC "
 514                             "machines\n"), subcmd);
 515                         ret = BAM_ERROR;
 516                 } else {
 517                         ret = f(&menu, bam_root, NULL);
 518                 }
 519         } else if (strcmp(subcmd, "enable_hypervisor") == 0) {
 520                 if (is_sparc()) {
 521                         bam_error(_("%s operation unsupported on SPARC "
 522                             "machines\n"), subcmd);
 523                         ret = BAM_ERROR;
 524                 } else {
 525                         char *extra_args = NULL;
 526 
 527                         /*
 528                          * Compress all arguments passed in the largv[] array
 529                          * into one string that can then be appended to the
 530                          * end of the kernel$ string the routine to enable the
 531                          * hypervisor will build.
 532                          *
 533                          * This allows the caller to supply arbitrary unparsed
 534                          * arguments, such as dom0 memory settings or APIC
 535                          * options.
 536                          *
 537                          * This concatenation will be done without ANY syntax
 538                          * checking whatsoever, so it's the responsibility of
 539                          * the caller to make sure the arguments are valid and
 540                          * do not duplicate arguments the conversion routines
 541                          * may create.
 542                          */
 543                         if (largc > 0) {
 544                                 int extra_len, i;
 545 
 546                                 for (extra_len = 0, i = 0; i < largc; i++)
 547                                         extra_len += strlen(largv[i]);
 548 
 549                                 /*
 550                                  * Allocate space for argument strings,
 551                                  * intervening spaces and terminating NULL.
 552                                  */
 553                                 extra_args = alloca(extra_len + largc);
 554 
 555                                 (void) strcpy(extra_args, largv[0]);
 556 
 557                                 for (i = 1; i < largc; i++) {
 558                                         (void) strcat(extra_args, " ");
 559                                         (void) strcat(extra_args, largv[i]);
 560                                 }
 561                         }
 562 
 563                         ret = f(&menu, bam_root, extra_args);
 564                 }
 565         } else
 566                 ret = f(&menu, NULL, opt);
 567 
 568         if (ret == BAM_WRITE) {
 569                 BAM_DPRINTF(("%s: writing menu to clean-menu-root: <%s>\n",
 570                     fcn, clean_menu_root));
 571                 /* ret = menu_write(clean_menu_root, menu); */
 572         }
 573 
 574         INJECT_ERROR1("POOL_SET", pool = "/pooldata");
 575         assert((is_zfs(menu_root)) ^ (pool == NULL));
 576         if (pool) {
 577                 (void) umount_top_dataset(pool, zmnted, zmntpt);
 578                 free(special);
 579         }
 580 
 581         menu_free(&menu);
 582         return (ret);
 583 }
 584 
 585 /*
 586  * To suppress output from ficl. We do not want to see messages
 587  * from interpreting loader config.
 588  */
 589 
 590 /*ARGSUSED*/
 591 static void
 592 ficlTextOutSilent(ficlCallback *cb, char *text)
 593 {
 594 }
 595 
 596 /*ARGSUSED*/
 597 static error_t
 598 set_option(struct menu_lst *menu, char *dummy, char *opt)
 599 {
 600         char path[PATH_MAX];
 601         char *val;
 602         char *rest;
 603         int optval;
 604         menu_entry_t *entry;
 605         nvlist_t *be_attrs;
 606         FILE *fp;
 607         int rv, ret = BAM_SUCCESS;
 608 
 609         assert(menu);
 610         assert(opt);
 611         assert(dummy == NULL);
 612 
 613         val = strchr(opt, '=');
 614         if (val != NULL) {
 615                 *val++ = '\0';
 616         }
 617 
 618         if (strcmp(opt, "default") == 0) {
 619                 errno = 0;
 620                 optval = strtol(val, &rest, 10);
 621                 if (errno != 0 || *rest != '\0') {
 622                         bam_error(_("invalid boot entry number: %s\n"), val);
 623                         return (BAM_ERROR);
 624                 }
 625                 STAILQ_FOREACH(entry, menu, me_next) {
 626                         if (entry->me_idx == optval)
 627                                 break;
 628                 }
 629                 if (entry == NULL) {
 630                         bam_error(_("invalid boot entry number: %s\n"), val);
 631                         return (BAM_ERROR);
 632                 }
 633                 if (nvlist_alloc(&be_attrs, NV_UNIQUE_NAME, 0) != 0) {
 634                         bam_error(_("out of memory\n"));
 635                         return (BAM_ERROR);
 636                 }
 637                 if (nvlist_add_string(be_attrs, BE_ATTR_ORIG_BE_NAME,
 638                     entry->me_title) != 0) {
 639                         bam_error(_("out of memory\n"));
 640                         nvlist_free(be_attrs);
 641                         return (BAM_ERROR);
 642                 }
 643                 ret = be_activate(be_attrs);
 644                 nvlist_free(be_attrs);
 645                 if (ret != 0)
 646                         ret = BAM_ERROR;
 647                 return (ret);
 648         } else if (strcmp(opt, "timeout") == 0) {
 649                 errno = 0;
 650                 optval = strtol(val, &rest, 10);
 651                 if (errno != 0 || *rest != '\0') {
 652                         bam_error(_("invalid timeout: %s\n"), val);
 653                         return (BAM_ERROR);
 654                 }
 655 
 656                 (void) snprintf(path, PATH_MAX, "%s" CONF_DIR "/timeout",
 657                     bam_root);
 658 
 659                 fp = fopen(path, "w");
 660                 if (fp == NULL) {
 661                         bam_error(_("failed to open file: %s: %s\n"),
 662                             path, strerror(errno));
 663                         return (BAM_ERROR);
 664                 }
 665                 /*
 666                  * timeout=-1 is to disable auto boot in illumos, but
 667                  * loader needs "NO" to disable auto boot.
 668                  */
 669                 if (optval == -1)
 670                         rv = fprintf(fp, "autoboot_delay=\"NO\"\n");
 671                 else
 672                         rv = fprintf(fp, "autoboot_delay=\"%d\"\n", optval);
 673 
 674                 if (rv < 0) {
 675                         bam_error(_("write to file failed: %s: %s\n"),
 676                             path, strerror(errno));
 677                         (void) fclose(fp);
 678                         ret = BAM_ERROR;
 679                 } else
 680                         rv = fclose(fp);
 681 
 682                 if (rv < 0) {
 683                         bam_error(_("failed to close file: %s: %s\n"),
 684                             path, strerror(errno));
 685                         ret = BAM_ERROR;
 686                 }
 687                 if (ret == BAM_ERROR)
 688                         (void) unlink(path);
 689 
 690                 return (BAM_SUCCESS);
 691         }
 692 
 693         bam_error(_("invalid option: %s\n"), opt);
 694         return (BAM_ERROR);
 695 }
 696 
 697 static int
 698 bam_mount_be(menu_entry_t *entry, char **dir)
 699 {
 700         nvlist_t *be_attrs = NULL;
 701         const char *tmpdir = getenv("TMPDIR");
 702         const char *tmpname = "bam.XXXXXX";
 703         be_node_list_t *be_node, *be_nodes = NULL;
 704         int ret;
 705 
 706         *dir = NULL;
 707         if (tmpdir == NULL)
 708                 tmpdir = "/tmp";
 709 
 710         ret = asprintf(dir, "%s/%s", tmpdir, tmpname);
 711         if (ret < 0) {
 712                 return (BE_ERR_NOMEM);
 713         }
 714         *dir = mkdtemp(*dir);
 715 
 716         if (nvlist_alloc(&be_attrs, NV_UNIQUE_NAME, 0) != 0) {
 717                 ret = BE_ERR_NOMEM;
 718                 goto out;
 719         }
 720 
 721         ret = be_list(NULL, &be_nodes);
 722         if (ret != BE_SUCCESS) {
 723                 goto out;
 724         }
 725 
 726         for (be_node = be_nodes; be_node;
 727             be_node = be_node->be_next_node)
 728                 if (strcmp(be_node->be_root_ds, entry->me_bootfs) == 0)
 729                         break;
 730 
 731         if (nvlist_add_string(be_attrs, BE_ATTR_ORIG_BE_NAME,
 732             be_node->be_node_name) != 0) {
 733                 ret = BE_ERR_NOMEM;
 734                 goto out;
 735         }
 736 
 737         if (nvlist_add_string(be_attrs, BE_ATTR_MOUNTPOINT, *dir) != 0) {
 738                 ret = BE_ERR_NOMEM;
 739                 goto out;
 740         }
 741 
 742         ret = be_mount(be_attrs);
 743         if (ret == BE_ERR_MOUNTED) {
 744                 /*
 745                  * if BE is mounted, dir does not point to correct directory
 746                  */
 747                 (void) rmdir(*dir);
 748                 free(*dir);
 749                 *dir = NULL;
 750         }
 751 out:
 752         if (be_nodes != NULL)
 753                 be_free_list(be_nodes);
 754         nvlist_free(be_attrs);
 755         return (ret);
 756 }
 757 
 758 static int
 759 bam_umount_be(char *dir)
 760 {
 761         nvlist_t *be_attrs;
 762         int ret;
 763 
 764         if (dir == NULL)                /* nothing to do */
 765                 return (BE_SUCCESS);
 766 
 767         if (nvlist_alloc(&be_attrs, NV_UNIQUE_NAME, 0) != 0)
 768                 return (BE_ERR_NOMEM);
 769 
 770         if (nvlist_add_string(be_attrs, BE_ATTR_ORIG_BE_NAME, dir) != 0) {
 771                 ret = BE_ERR_NOMEM;
 772                 goto out;
 773         }
 774 
 775         ret = be_unmount(be_attrs);
 776 out:
 777         nvlist_free(be_attrs);
 778         return (ret);
 779 }
 780 
 781 /*
 782  * display details of menu entry or single property
 783  */
 784 static error_t
 785 list_menu_entry(menu_entry_t *entry, char *setting)
 786 {
 787         int ret = BAM_SUCCESS;
 788         char *ptr, *dir;
 789         char buf[MAX_INPUT];
 790         ficlVm *vm;
 791         int mounted;
 792 
 793         if (strcmp(entry->me_type, "bootfs") != 0 ||
 794             strchr(entry->me_bootfs, ':') != NULL) {
 795                 (void) printf("\nTitle:       %s\n", entry->me_title);
 796                 (void) printf("Type:        %s\n", entry->me_type);
 797                 (void) printf("Device:      %s\n", entry->me_bootfs);
 798                 return (ret);
 799         }
 800 
 801         mounted = bam_mount_be(entry, &dir);
 802         if (mounted != BE_SUCCESS && mounted != BE_ERR_MOUNTED) {
 803                 if (dir != NULL) {
 804                         (void) rmdir(dir);
 805                         free(dir);
 806                 }
 807                 bam_error(_("%s is not mounted\n"), entry->me_title);
 808                 return (BAM_ERROR);
 809         }
 810 
 811         vm = bf_init("", ficlTextOutSilent);
 812         if (vm == NULL) {
 813                 bam_error(_("error setting up forth interpreter\n"));
 814                 ret = BAM_ERROR;
 815                 goto done;
 816         }
 817 
 818         /* should only get FICL_VM_STATUS_OUT_OF_TEXT */
 819         (void) snprintf(buf, MAX_INPUT, "set currdev=zfs:%s:",
 820             entry->me_bootfs);
 821         ret = ficlVmEvaluate(vm, buf);
 822         if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
 823                 bam_error(_("error interpreting boot config\n"));
 824                 ret = BAM_ERROR;
 825                 goto done;
 826         }
 827         (void) snprintf(buf, MAX_INPUT, "include /boot/forth/loader.4th");
 828         ret = ficlVmEvaluate(vm, buf);
 829         if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
 830                 bam_error(_("error interpreting boot config\n"));
 831                 ret = BAM_ERROR;
 832                 goto done;
 833         }
 834         (void) snprintf(buf, MAX_INPUT, "start");
 835         ret = ficlVmEvaluate(vm, buf);
 836         if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
 837                 bam_error(_("error interpreting boot config\n"));
 838                 ret = BAM_ERROR;
 839                 goto done;
 840         }
 841         (void) snprintf(buf, MAX_INPUT, "boot");
 842         ret = ficlVmEvaluate(vm, buf);
 843         if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
 844                 bam_error(_("error interpreting boot config\n"));
 845                 ret = BAM_ERROR;
 846                 goto done;
 847         }
 848 
 849         ret = BAM_SUCCESS;
 850         if (*setting == '\0')
 851                 (void) printf("\nTitle:       %s\n", entry->me_title);
 852         else if (strcasecmp(setting, "title") == 0) {
 853                 (void) printf("%s\n", entry->me_title);
 854                 goto done;
 855         }
 856 
 857         ptr = getenv("autoboot_delay");
 858         if (ptr != NULL) {
 859                 char *timeout = "-1";
 860 
 861                 if (strcasecmp(ptr, "NO") != 0)
 862                         timeout = ptr;
 863 
 864                 if (*setting == '\0')
 865                         (void) printf("Timeout:     %s\n", timeout);
 866                 else if (strcasecmp(setting, "timeout") == 0) {
 867                         (void) printf("%s\n", timeout);
 868                         goto done;
 869                 }
 870 
 871         }
 872         ptr = getenv("console");
 873         if (ptr != NULL) {
 874                 if (*setting == '\0')
 875                         (void) printf("Console:     %s\n", ptr);
 876                 else if (strcasecmp(setting, "console") == 0) {
 877                         (void) printf("%s\n", ptr);
 878                         goto done;
 879                 }
 880         }
 881 
 882         if (*setting == '\0')
 883                 (void) printf("Bootfs:      %s\n", entry->me_bootfs);
 884         else if (strcasecmp(setting, "bootfs") == 0) {
 885                 (void) printf("%s\n", entry->me_bootfs);
 886                 goto done;
 887         }
 888 
 889         ptr = getenv("xen_kernel");
 890         if (ptr != NULL) {
 891                         if (*setting == '\0') {
 892                                 (void) printf("Xen kernel:  %s\n", ptr);
 893                         } else if (strcasecmp(setting, "xen_kernel") == 0) {
 894                                 (void) printf("%s\n", ptr);
 895                                 goto done;
 896                         }
 897 
 898                         if (*setting == '\0') {
 899                                 (void) printf("Xen args:    \"%s\"\n",
 900                                     getenv("xen_cmdline"));
 901                         } else if (strcasecmp(setting, "xen_cmdline") == 0) {
 902                                 (void) printf("%s\n", getenv("xen_cmdline"));
 903                                 goto done;
 904                         }
 905 
 906                         if (*setting == '\0') {
 907                                 (void) printf("Kernel:      %s\n",
 908                                     getenv("bootfile"));
 909                         } if (strcasecmp(setting, "kernel") == 0) {
 910                                 (void) printf("%s\n", getenv("bootfile"));
 911                                 goto done;
 912                         }
 913         } else {
 914                 ptr = getenv("kernelname");
 915                 if (ptr != NULL) {
 916                         if (*setting == '\0') {
 917                                 (void) printf("Kernel:      %s\n", ptr);
 918                         } else if (strcasecmp(setting, "kernel") == 0) {
 919                                 (void) printf("%s\n", ptr);
 920                                 goto done;
 921                         }
 922                 }
 923         }
 924 
 925         ptr = getenv("boot-args");
 926         if (ptr != NULL) {
 927                 if (*setting == '\0') {
 928                         (void) printf("Boot-args:   \"%s\"\n", ptr);
 929                 } else if (strcasecmp(setting, "boot-args") == 0) {
 930                         (void) printf("%s\n", ptr);
 931                         goto done;
 932                 }
 933         }
 934 
 935         if (*setting == '\0' || strcasecmp(setting, "modules") == 0) {
 936                 (void) printf("\nModules:\n");
 937                 ficlVmSetTextOut(vm, ficlCallbackDefaultTextOut);
 938                 (void) snprintf(buf, MAX_INPUT, "show-module-options");
 939                 ret = ficlVmEvaluate(vm, buf);
 940                 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
 941                         bam_error(_("error interpreting boot config\n"));
 942                         ret = BAM_ERROR;
 943                         goto done;
 944                 }
 945                 ret = BAM_SUCCESS;
 946                 goto done;
 947         }
 948 
 949         /* if we got here with setting string, its unknown property */
 950         if (*setting != '\0') {
 951                 bam_error(_("unknown property: %s\n"), setting);
 952                 ret = BAM_ERROR;
 953         } else
 954                 ret = BAM_SUCCESS;
 955 done:
 956         bf_fini();
 957         if (mounted != BE_ERR_MOUNTED) {
 958                 (void) bam_umount_be(dir);
 959         }
 960 
 961         if (dir != NULL) {
 962                 (void) rmdir(dir);
 963                 free(dir);
 964         }
 965 
 966         return (ret);
 967 }
 968 
 969 /*ARGSUSED*/
 970 static error_t
 971 list_entry(struct menu_lst *menu, char *menu_root, char *opt)
 972 {
 973         error_t ret = BAM_SUCCESS;
 974         menu_entry_t *entry;
 975         char *ptr, *title = NULL;
 976         int i, e = -1;
 977 
 978         if (opt == NULL) {
 979                 print_nodes(B_FALSE, menu);
 980                 return (ret);
 981         }
 982 
 983         if ((ptr = strchr(opt, '=')) == NULL) {
 984                 bam_error(_("invalid option: %s\n"), opt);
 985                 return (BAM_ERROR);
 986         }
 987 
 988         i = ptr - opt;
 989         if (strncmp(opt, "entry", i) == 0) {
 990                 e = atoi(ptr+1);
 991         } else if (strncmp(opt, "title", i) == 0) {
 992                 title = ptr+1;
 993         } else {
 994                 bam_error(_("invalid option: %s\n"), opt);
 995                 return (BAM_ERROR);
 996         }
 997 
 998         STAILQ_FOREACH(entry, menu, me_next) {
 999                 if (title != NULL) {
1000                         if (strcmp(title, entry->me_title) == 0)
1001                                 break;
1002                 } else if (entry->me_idx == e)
1003                         break;
1004         }
1005 
1006         if (entry == NULL) {
1007                 bam_error(_("no matching entry found\n"));
1008                 return (BAM_ERROR);
1009         }
1010 
1011         return (list_menu_entry(entry, ""));
1012 }
1013 
1014 /*
1015  * For now this is just stub entry to support grub interface, the
1016  * known consumer is installer ict.py code, calling as:
1017  * bootadm update-menu -R /a -Z -o rdisk
1018  * Later this can be converted to do something useful.
1019  */
1020 /*ARGSUSED*/
1021 static error_t
1022 update_entry(struct menu_lst *menu, char *menu_root, char *osdev)
1023 {
1024         char path[PATH_MAX];
1025         char *pool = menu_root + 1;
1026         be_node_list_t *be_nodes, *be_node;
1027         int rv;
1028         FILE *fp;
1029 
1030         (void) snprintf(path, PATH_MAX, "%s%s", menu_root, MENU);
1031         rv = be_list(NULL, &be_nodes);
1032 
1033         if (rv != BE_SUCCESS)
1034                 return (BAM_ERROR);
1035 
1036         fp = fopen(path, "w");
1037         if (fp == NULL) {
1038                 be_free_list(be_nodes);
1039                 return (BAM_ERROR);
1040         }
1041 
1042         for (be_node = be_nodes; be_node; be_node = be_node->be_next_node) {
1043                 if (strcmp(be_node->be_rpool, pool) == 0) {
1044                         (void) fprintf(fp, "title %s\n", be_node->be_node_name);
1045                         (void) fprintf(fp, "bootfs %s\n", be_node->be_root_ds);
1046                 }
1047         }
1048 
1049         be_free_list(be_nodes);
1050         (void) fclose(fp);
1051         return (BAM_SUCCESS);
1052 }
1053 
1054 /*ARGSUSED*/
1055 static error_t
1056 update_temp(struct menu_lst *menu, char *dummy, char *opt)
1057 {
1058         error_t ret = BAM_ERROR;
1059         char path[PATH_MAX];
1060         char buf[MAX_INPUT];
1061         struct mnttab mpref = { 0 };
1062         struct mnttab mp = { 0 };
1063         ficlVm *vm;
1064         char *env, *o;
1065         FILE *fp;
1066 
1067         (void) snprintf(path, PATH_MAX, "%s" TRANSIENT, bam_root);
1068         /*
1069          * if opt == NULL, remove transient config
1070          */
1071         if (opt == NULL) {
1072                 (void) unlink(path);
1073                 return (BAM_SUCCESS);
1074         }
1075 
1076         fp = fopen(MNTTAB, "r");
1077         if (fp == NULL)
1078                 return (BAM_ERROR);
1079 
1080         mpref.mnt_mountp = "/";
1081         if (getmntany(fp, &mp, &mpref) != 0) {
1082                 (void) fclose(fp);
1083                 return (BAM_ERROR);
1084         }
1085         (void) fclose(fp);
1086 
1087         vm = bf_init("", ficlTextOutSilent);
1088         if (vm == NULL) {
1089                 bam_error(_("Error setting up forth interpreter\n"));
1090                 return (ret);
1091         }
1092 
1093         /*
1094          * need to check current boot config, so fire up the ficl
1095          * if its xen setup, we add option to boot-args list, not replacing it.
1096          */
1097         (void) snprintf(buf, MAX_INPUT, "set currdev=zfs:%s:", mp.mnt_special);
1098         ret = ficlVmEvaluate(vm, buf);
1099         if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
1100                 bam_error(_("Error interpreting boot config\n"));
1101                 bf_fini();
1102                 return (BAM_ERROR);
1103         }
1104         (void) snprintf(buf, MAX_INPUT, "include /boot/forth/loader.4th");
1105         ret = ficlVmEvaluate(vm, buf);
1106         if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
1107                 bam_error(_("Error interpreting boot config\n"));
1108                 bf_fini();
1109                 return (BAM_ERROR);
1110         }
1111         (void) snprintf(buf, MAX_INPUT, "start");
1112         ret = ficlVmEvaluate(vm, buf);
1113         if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
1114                 bam_error(_("Error interpreting boot config\n"));
1115                 bf_fini();
1116                 return (BAM_ERROR);
1117         }
1118         (void) snprintf(buf, MAX_INPUT, "boot");
1119         ret = ficlVmEvaluate(vm, buf);
1120         if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
1121                 bam_error(_("Error interpreting boot config\n"));
1122                 bf_fini();
1123                 return (BAM_ERROR);
1124         }
1125         bf_fini();
1126 
1127         if (opt[0] == '-') {
1128                 env = getenv("xen_kernel");
1129                 fp = fopen(path, "w");
1130                 if (fp == NULL)
1131                         return (BAM_ERROR);
1132 
1133                 if (env != NULL) {
1134                         env = getenv("boot-args");
1135                         (void) fprintf(fp, "boot-args=\"%s %s\"\n", env, opt);
1136                 } else
1137                         (void) fprintf(fp, "boot-args=\"%s\"\n", opt);
1138                 (void) fclose(fp);
1139                 return (BAM_SUCCESS);
1140         }
1141 
1142         /*
1143          * it should be the case with "kernel args"
1144          * so, we split the opt at first space
1145          * and store bootfile= and boot-args=
1146          */
1147         env = getenv("xen_kernel");
1148 
1149         o = strchr(opt, ' ');
1150         if (o == NULL) {
1151                 fp = fopen(path, "w");
1152                 if (fp == NULL)
1153                         return (BAM_ERROR);
1154                 (void) fprintf(fp, "bootfile=\"%s;unix\"\n", opt);
1155                 (void) fclose(fp);
1156                 return (BAM_SUCCESS);
1157         }
1158         *o++ = '\0';
1159         fp = fopen(path, "w");
1160         if (fp == NULL)
1161                 return (BAM_ERROR);
1162         (void) fprintf(fp, "bootfile=\"%s;unix\"\n", opt);
1163 
1164         if (env != NULL) {
1165                 env = getenv("boot-args");
1166                 (void) fprintf(fp, "boot-args=\"%s %s\"\n", env, opt);
1167         } else
1168                 (void) fprintf(fp, "boot-args=\"%s\"\n", o);
1169 
1170         (void) fflush(fp);
1171         (void) fclose(fp);
1172         return (ret);
1173 }
1174 
1175 static error_t
1176 list_setting(struct menu_lst *menu, char *which, char *setting)
1177 {
1178         int entry = -1;
1179         menu_entry_t *m;
1180         be_node_list_t *be_nodes, *be_node = NULL;
1181         int ret;
1182 
1183         assert(which);
1184         assert(setting);
1185 
1186         /*
1187          * which can be:
1188          * "" - list default entry
1189          * number - use for entry number
1190          * property name
1191          */
1192         if (*which != '\0') {
1193                 if (isdigit(*which)) {
1194                         char *rest;
1195                         errno = 0;
1196                         entry = strtol(which, &rest, 10);
1197                         if (errno != 0 || *rest != '\0') {
1198                                 bam_error(_("invalid boot entry number: %s\n"),
1199                                     which);
1200                                 return (BAM_ERROR);
1201                         }
1202                 } else
1203                         setting = which;
1204         }
1205 
1206         /* find default entry */
1207         if (entry == -1) {
1208                 ret = be_list(NULL, &be_nodes);
1209                 if (ret != BE_SUCCESS) {
1210                         bam_error(_("No BE's found\n"));
1211                         return (BAM_ERROR);
1212                 }
1213                 STAILQ_FOREACH(m, menu, me_next) {
1214                         entry++;
1215                         for (be_node = be_nodes; be_node;
1216                             be_node = be_node->be_next_node) {
1217                                 if (strcmp(be_node->be_root_ds,
1218                                     m->me_bootfs) == 0)
1219                                         break;
1220                         }
1221                         if (be_node != NULL &&
1222                             be_node->be_active_on_boot == B_TRUE)
1223                                 break; /* found active node */
1224                 }
1225                 be_free_list(be_nodes);
1226                 if (be_node == NULL) {
1227                         bam_error(_("None of BE nodes is marked active\n"));
1228                         return (BAM_ERROR);
1229                 }
1230         } else {
1231                 STAILQ_FOREACH(m, menu, me_next)
1232                         if (m->me_idx == entry)
1233                                 break;
1234 
1235                 if (m == NULL) {
1236                         bam_error(_("no matching entry found\n"));
1237                         return (BAM_ERROR);
1238                 }
1239         }
1240 
1241         return (list_menu_entry(m, setting));
1242 }
1243 
1244 /*ARGSUSED*/
1245 static error_t
1246 disable_hyper(struct menu_lst *menu, char *osroot, char *opt)
1247 {
1248         char path[PATH_MAX];
1249 
1250         (void) snprintf(path, PATH_MAX, "%s" XEN_CONFIG, bam_root);
1251         (void) unlink(path);
1252         return (BAM_SUCCESS);
1253 }
1254 
1255 /*ARGSUSED*/
1256 static error_t
1257 enable_hyper(struct menu_lst *menu, char *osroot, char *opt)
1258 {
1259         ficlVm *vm;
1260         char path[PATH_MAX];
1261         char buf[MAX_INPUT];
1262         char *env;
1263         FILE *fp;
1264         struct mnttab mpref = { 0 };
1265         struct mnttab mp = { 0 };
1266         int ret;
1267 
1268         fp = fopen(MNTTAB, "r");
1269         if (fp == NULL)
1270                 return (BAM_ERROR);
1271 
1272         mpref.mnt_mountp = "/";
1273         if (getmntany(fp, &mp, &mpref) != 0) {
1274                 (void) fclose(fp);
1275                 return (BAM_ERROR);
1276         }
1277         (void) fclose(fp);
1278 
1279         vm = bf_init("", ficlTextOutSilent);
1280         if (vm == NULL) {
1281                 bam_error(_("Error setting up forth interpreter\n"));
1282                 return (BAM_ERROR);
1283         }
1284 
1285         /*
1286          * need to check current boot config, so fire up the ficl
1287          * if its xen setup, we add option to boot-args list, not replacing it.
1288          */
1289         (void) snprintf(buf, MAX_INPUT, "set currdev=zfs:%s:", mp.mnt_special);
1290         ret = ficlVmEvaluate(vm, buf);
1291         if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
1292                 bam_error(_("Error interpreting boot config\n"));
1293                 bf_fini();
1294                 return (BAM_ERROR);
1295         }
1296         (void) snprintf(buf, MAX_INPUT, "include /boot/forth/loader.4th");
1297         ret = ficlVmEvaluate(vm, buf);
1298         if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
1299                 bam_error(_("Error interpreting boot config\n"));
1300                 bf_fini();
1301                 return (BAM_ERROR);
1302         }
1303         (void) snprintf(buf, MAX_INPUT, "start");
1304         ret = ficlVmEvaluate(vm, buf);
1305         if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
1306                 bam_error(_("Error interpreting boot config\n"));
1307                 bf_fini();
1308                 return (BAM_ERROR);
1309         }
1310         (void) snprintf(buf, MAX_INPUT, "boot");
1311         ret = ficlVmEvaluate(vm, buf);
1312         if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
1313                 bam_error(_("Error interpreting boot config\n"));
1314                 bf_fini();
1315                 return (BAM_ERROR);
1316         }
1317         bf_fini();
1318 
1319         (void) mkdir(CONF_DIR, 0755);
1320         (void) snprintf(path, PATH_MAX, "%s" XEN_CONFIG, bam_root);
1321         fp = fopen(path, "w");
1322         if (fp == NULL) {
1323                 return (BAM_ERROR);     /* error, cant write config */
1324         }
1325 
1326         errno = 0;
1327         /*
1328          * on write error, remove file to ensure we have bootable config.
1329          * note we dont mind if config exists, it will get updated
1330          */
1331         (void) fprintf(fp, "xen_kernel=\"/boot/${ISADIR}/xen\"\n");
1332         if (errno != 0)
1333                 goto error;
1334 
1335         /*
1336          * really simple and stupid console conversion.
1337          * it really has to be gone, it belongs to milestone/xvm properties.
1338          */
1339         env = getenv("console");
1340         if (env != NULL) {
1341                 if (strcmp(env, "ttya") == 0)
1342                         (void) fprintf(fp, "xen_cmdline=\"console=com1 %s\"\n",
1343                             opt);
1344                 else if (strcmp(env, "ttyb") == 0)
1345                         (void) fprintf(fp, "xen_cmdline=\"console=com2 %s\"\n",
1346                             opt);
1347                 else
1348                         (void) fprintf(fp, "xen_cmdline=\"console=vga %s\"\n",
1349                             opt);
1350         } else
1351                 (void) fprintf(fp, "xen_cmdline=\"%s\"\n", opt);
1352         if (errno != 0)
1353                 goto error;
1354 
1355         (void) fprintf(fp,
1356             "bootfile=\"/platform/i86xpv/kernel/${ISADIR}/unix\"\n");
1357         if (errno != 0)
1358                 goto error;
1359 
1360         (void) fprintf(fp,
1361             "boot-args=\"/platform/i86xpv/kernel/${ISADIR}/unix\"\n");
1362         if (errno != 0)
1363                 goto error;
1364 
1365         (void) fclose(fp);
1366         if (errno != 0) {
1367                 (void) unlink(path);
1368                 return (BAM_ERROR);
1369         }
1370         return (BAM_SUCCESS);
1371 error:
1372         (void) fclose(fp);
1373         (void) unlink(path);
1374         return (BAM_ERROR);
1375 }