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 /*
  23  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
  24  * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
  25  * Copyright 2016 Toomas Soome <tsoome@me.com>
  26  * Copyright (c) 2015 by Delphix. All rights reserved.
  27  * Copyright 2018 OmniOS Community Edition (OmniOSce) Association.
  28  */
  29 
  30 
  31 /*
  32  * System includes
  33  */
  34 #include <assert.h>
  35 #include <errno.h>
  36 #include <libgen.h>
  37 #include <libintl.h>
  38 #include <libnvpair.h>
  39 #include <libzfs.h>
  40 #include <libgen.h>
  41 #include <stdio.h>
  42 #include <stdlib.h>
  43 #include <string.h>
  44 #include <sys/stat.h>
  45 #include <sys/types.h>
  46 #include <sys/vfstab.h>
  47 #include <sys/param.h>
  48 #include <sys/systeminfo.h>
  49 #include <ctype.h>
  50 #include <time.h>
  51 #include <unistd.h>
  52 #include <fcntl.h>
  53 #include <deflt.h>
  54 #include <wait.h>
  55 #include <libdevinfo.h>
  56 #include <libgen.h>
  57 
  58 #include <libbe.h>
  59 #include <libbe_priv.h>
  60 #include <boot_utils.h>
  61 #include <ficl.h>
  62 #include <ficlplatform/emu.h>
  63 
  64 /* Private function prototypes */
  65 static int update_dataset(char *, int, char *, char *, char *);
  66 static int _update_vfstab(char *, char *, char *, char *, be_fs_list_data_t *);
  67 static int be_open_menu(char *, char *, FILE **, char *, boolean_t);
  68 static int be_create_menu(char *, char *, FILE **, char *);
  69 static char *be_get_auto_name(char *, char *, boolean_t);
  70 
  71 /*
  72  * Global error printing
  73  */
  74 boolean_t do_print = B_FALSE;
  75 
  76 /*
  77  * Private datatypes
  78  */
  79 typedef struct zone_be_name_cb_data {
  80         char *base_be_name;
  81         int num;
  82 } zone_be_name_cb_data_t;
  83 
  84 /* ******************************************************************** */
  85 /*                      Public Functions                                */
  86 /* ******************************************************************** */
  87 
  88 /*
  89  * Callback for ficl to suppress all output from ficl, as we do not
  90  * want to confuse user with messages from ficl, and we are only
  91  * checking results from function calls.
  92  */
  93 /*ARGSUSED*/
  94 static void
  95 ficlSuppressTextOutput(ficlCallback *cb, char *text)
  96 {
  97         /* This function is intentionally doing nothing. */
  98 }
  99 
 100 /*
 101  * Function:    be_get_boot_args
 102  * Description: Returns the fast boot argument string for enumerated BE.
 103  * Parameters:
 104  *              fbarg - pointer to argument string.
 105  *              entry - index of BE.
 106  * Returns:
 107  *              fast boot argument string.
 108  * Scope:
 109  *              Public
 110  */
 111 int
 112 be_get_boot_args(char **fbarg, int entry)
 113 {
 114         be_node_list_t *node, *be_nodes = NULL;
 115         be_transaction_data_t bt = {0};
 116         char *mountpoint = NULL;
 117         boolean_t be_mounted = B_FALSE;
 118         int ret = BE_SUCCESS;
 119         int index;
 120         ficlVm *vm;
 121 
 122         *fbarg = NULL;
 123         if (!be_zfs_init())
 124                 return (BE_ERR_INIT);
 125 
 126         /*
 127          * need pool name, menu.lst has entries from our pool only
 128          */
 129         ret = be_find_current_be(&bt);
 130         if (ret != BE_SUCCESS) {
 131                 be_zfs_fini();
 132                 return (ret);
 133         }
 134 
 135         /*
 136          * be_get_boot_args() is for loader, fail with grub will trigger
 137          * normal boot.
 138          */
 139         if (be_has_grub()) {
 140                 ret = BE_ERR_INIT;
 141                 goto done;
 142         }
 143 
 144         ret = _be_list(NULL, &be_nodes, BE_LIST_DEFAULT);
 145         if (ret != BE_SUCCESS)
 146                 goto done;
 147 
 148         /*
 149          * iterate through be_nodes,
 150          * if entry == -1, stop if be_active_on_boot,
 151          * else stop if index == entry.
 152          */
 153         index = 0;
 154         for (node = be_nodes; node != NULL; node = node->be_next_node) {
 155                 if (strcmp(node->be_rpool, bt.obe_zpool) != 0)
 156                         continue;
 157                 if (entry == BE_ENTRY_DEFAULT &&
 158                     node->be_active_on_boot == B_TRUE)
 159                         break;
 160                 if (index == entry)
 161                         break;
 162                 index++;
 163         }
 164         if (node == NULL) {
 165                 be_free_list(be_nodes);
 166                 ret = BE_ERR_NOENT;
 167                 goto done;
 168         }
 169 
 170         /* try to mount inactive be */
 171         if (node->be_active == B_FALSE) {
 172                 ret = _be_mount(node->be_node_name, &mountpoint,
 173                     BE_MOUNT_FLAG_NO_ZONES);
 174                 if (ret != BE_SUCCESS && ret != BE_ERR_MOUNTED) {
 175                         be_free_list(be_nodes);
 176                         goto done;
 177                 } else
 178                         be_mounted = B_TRUE;
 179         }
 180 
 181         vm = bf_init("", ficlSuppressTextOutput);
 182         if (vm != NULL) {
 183                 /*
 184                  * zfs MAXNAMELEN is 256, so we need to pick buf large enough
 185                  * to contain such names.
 186                  */
 187                 char buf[MAXNAMELEN * 2];
 188                 char *kernel_options = NULL;
 189                 char *kernel = NULL;
 190                 char *tmp;
 191                 zpool_handle_t *zph;
 192 
 193                 /*
 194                  * just try to interpret following words. on error
 195                  * we will be missing kernelname, and will get out.
 196                  */
 197                 (void) snprintf(buf, sizeof (buf), "set currdev=zfs:%s:",
 198                     node->be_root_ds);
 199                 ret = ficlVmEvaluate(vm, buf);
 200                 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
 201                         be_print_err(gettext("be_get_boot_args: error "
 202                             "interpreting boot config: %d\n"), ret);
 203                         bf_fini();
 204                         ret = BE_ERR_NO_MENU;
 205                         goto cleanup;
 206                 }
 207                 (void) snprintf(buf, sizeof (buf),
 208                     "include /boot/forth/loader.4th");
 209                 ret = ficlVmEvaluate(vm, buf);
 210                 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
 211                         be_print_err(gettext("be_get_boot_args: error "
 212                             "interpreting boot config: %d\n"), ret);
 213                         bf_fini();
 214                         ret = BE_ERR_NO_MENU;
 215                         goto cleanup;
 216                 }
 217                 (void) snprintf(buf, sizeof (buf), "start");
 218                 ret = ficlVmEvaluate(vm, buf);
 219                 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
 220                         be_print_err(gettext("be_get_boot_args: error "
 221                             "interpreting boot config: %d\n"), ret);
 222                         bf_fini();
 223                         ret = BE_ERR_NO_MENU;
 224                         goto cleanup;
 225                 }
 226                 (void) snprintf(buf, sizeof (buf), "boot");
 227                 ret = ficlVmEvaluate(vm, buf);
 228                 bf_fini();
 229                 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
 230                         be_print_err(gettext("be_get_boot_args: error "
 231                             "interpreting boot config: %d\n"), ret);
 232                         ret = BE_ERR_NO_MENU;
 233                         goto cleanup;
 234                 }
 235 
 236                 kernel_options = getenv("boot-args");
 237                 kernel = getenv("kernelname");
 238 
 239                 if (kernel == NULL) {
 240                         be_print_err(gettext("be_get_boot_args: no kernel\n"));
 241                         ret = BE_ERR_NOENT;
 242                         goto cleanup;
 243                 }
 244 
 245                 if ((zph = zpool_open(g_zfs, node->be_rpool)) == NULL) {
 246                         be_print_err(gettext("be_get_boot_args: failed to "
 247                             "open root pool (%s): %s\n"), node->be_rpool,
 248                             libzfs_error_description(g_zfs));
 249                         ret = zfs_err_to_be_err(g_zfs);
 250                         goto cleanup;
 251                 }
 252                 ret = zpool_get_physpath(zph, buf, sizeof (buf));
 253                 zpool_close(zph);
 254                 if (ret != 0) {
 255                         be_print_err(gettext("be_get_boot_args: failed to "
 256                             "get physpath\n"));
 257                         goto cleanup;
 258                 }
 259 
 260                 /* zpool_get_physpath() can return space separated list */
 261                 tmp = buf;
 262                 tmp = strsep(&tmp, " ");
 263 
 264                 if (kernel_options == NULL || *kernel_options == '\0')
 265                         (void) asprintf(fbarg, "/ %s "
 266                             "-B zfs-bootfs=%s,bootpath=\"%s\"\n", kernel,
 267                             node->be_root_ds, tmp);
 268                 else
 269                         (void) asprintf(fbarg, "/ %s %s "
 270                             "-B zfs-bootfs=%s,bootpath=\"%s\"\n", kernel,
 271                             kernel_options, node->be_root_ds, tmp);
 272 
 273                 if (fbarg == NULL)
 274                         ret = BE_ERR_NOMEM;
 275                 else
 276                         ret = 0;
 277         } else
 278                 ret = BE_ERR_NOMEM;
 279 cleanup:
 280         if (be_mounted == B_TRUE)
 281                 (void) _be_unmount(node->be_node_name, BE_UNMOUNT_FLAG_FORCE);
 282         be_free_list(be_nodes);
 283 done:
 284         free(mountpoint);
 285         free(bt.obe_name);
 286         free(bt.obe_root_ds);
 287         free(bt.obe_zpool);
 288         free(bt.obe_snap_name);
 289         free(bt.obe_altroot);
 290         be_zfs_fini();
 291         return (ret);
 292 }
 293 
 294 /*
 295  * Function:    be_max_avail
 296  * Description: Returns the available size for the zfs dataset passed in.
 297  * Parameters:
 298  *              dataset - The dataset we want to get the available space for.
 299  *              ret - The available size will be returned in this.
 300  * Returns:
 301  *              The error returned by the zfs get property function.
 302  * Scope:
 303  *              Public
 304  */
 305 int
 306 be_max_avail(char *dataset, uint64_t *ret)
 307 {
 308         zfs_handle_t *zhp;
 309         int err = 0;
 310 
 311         /* Initialize libzfs handle */
 312         if (!be_zfs_init())
 313                 return (BE_ERR_INIT);
 314 
 315         zhp = zfs_open(g_zfs, dataset, ZFS_TYPE_DATASET);
 316         if (zhp == NULL) {
 317                 /*
 318                  * The zfs_open failed return an error
 319                  */
 320                 err = zfs_err_to_be_err(g_zfs);
 321         } else {
 322                 err = be_maxsize_avail(zhp, ret);
 323         }
 324         ZFS_CLOSE(zhp);
 325         be_zfs_fini();
 326         return (err);
 327 }
 328 
 329 /*
 330  * Function:    libbe_print_errors
 331  * Description: Turns on/off error output for the library.
 332  * Parameter:
 333  *              set_do_print - Boolean that turns library error
 334  *                             printing on or off.
 335  * Returns:
 336  *              None
 337  * Scope:
 338  *              Public;
 339  */
 340 void
 341 libbe_print_errors(boolean_t set_do_print)
 342 {
 343         do_print = set_do_print;
 344 }
 345 
 346 /* ******************************************************************** */
 347 /*                      Semi-Private Functions                          */
 348 /* ******************************************************************** */
 349 
 350 /*
 351  * Function:    be_zfs_init
 352  * Description: Initializes the libary global libzfs handle.
 353  * Parameters:
 354  *              None
 355  * Returns:
 356  *              B_TRUE - Success
 357  *              B_FALSE - Failure
 358  * Scope:
 359  *              Semi-private (library wide use only)
 360  */
 361 boolean_t
 362 be_zfs_init(void)
 363 {
 364         be_zfs_fini();
 365 
 366         if ((g_zfs = libzfs_init()) == NULL) {
 367                 be_print_err(gettext("be_zfs_init: failed to initialize ZFS "
 368                     "library\n"));
 369                 return (B_FALSE);
 370         }
 371 
 372         return (B_TRUE);
 373 }
 374 
 375 /*
 376  * Function:    be_zfs_fini
 377  * Description: Closes the library global libzfs handle if it currently open.
 378  * Parameter:
 379  *              None
 380  * Returns:
 381  *              None
 382  * Scope:
 383  *              Semi-private (library wide use only)
 384  */
 385 void
 386 be_zfs_fini(void)
 387 {
 388         if (g_zfs)
 389                 libzfs_fini(g_zfs);
 390 
 391         g_zfs = NULL;
 392 }
 393 
 394 /*
 395  * Function:    be_get_defaults
 396  * Description: Open defaults and gets be default paramets
 397  * Parameters:
 398  *              defaults - be defaults struct
 399  * Returns:
 400  *              None
 401  * Scope:
 402  *              Semi-private (library wide use only)
 403  */
 404 void
 405 be_get_defaults(struct be_defaults *defaults)
 406 {
 407         void    *defp;
 408 
 409         defaults->be_deflt_grub = B_FALSE;
 410         defaults->be_deflt_rpool_container = B_FALSE;
 411         defaults->be_deflt_bename_starts_with[0] = '\0';
 412 
 413         if ((defp = defopen_r(BE_DEFAULTS)) != NULL) {
 414                 const char *res = defread_r(BE_DFLT_BENAME_STARTS, defp);
 415                 if (res != NULL && res[0] != '\0') {
 416                         (void) strlcpy(defaults->be_deflt_bename_starts_with,
 417                             res, ZFS_MAX_DATASET_NAME_LEN);
 418                         defaults->be_deflt_rpool_container = B_TRUE;
 419                 }
 420                 if (be_is_isa("i386")) {
 421                         res = defread_r(BE_DFLT_BE_HAS_GRUB, defp);
 422                         if (res != NULL && res[0] != '\0') {
 423                                 if (strcasecmp(res, "true") == 0)
 424                                         defaults->be_deflt_grub = B_TRUE;
 425                         }
 426                 }
 427                 defclose_r(defp);
 428         }
 429 }
 430 
 431 /*
 432  * Function:    be_make_root_ds
 433  * Description: Generate string for BE's root dataset given the pool
 434  *              it lives in and the BE name.
 435  * Parameters:
 436  *              zpool - pointer zpool name.
 437  *              be_name - pointer to BE name.
 438  *              be_root_ds - pointer to buffer to return BE root dataset in.
 439  *              be_root_ds_size - size of be_root_ds
 440  * Returns:
 441  *              None
 442  * Scope:
 443  *              Semi-private (library wide use only)
 444  */
 445 void
 446 be_make_root_ds(const char *zpool, const char *be_name, char *be_root_ds,
 447     int be_root_ds_size)
 448 {
 449         struct be_defaults be_defaults;
 450         be_get_defaults(&be_defaults);
 451         char    *root_ds = NULL;
 452 
 453         if (getzoneid() == GLOBAL_ZONEID) {
 454                 if (be_defaults.be_deflt_rpool_container) {
 455                         (void) snprintf(be_root_ds, be_root_ds_size,
 456                             "%s/%s", zpool, be_name);
 457                 } else {
 458                         (void) snprintf(be_root_ds, be_root_ds_size,
 459                             "%s/%s/%s", zpool, BE_CONTAINER_DS_NAME, be_name);
 460                 }
 461         } else {
 462                 /*
 463                  * In non-global zone we can use path from mounted root dataset
 464                  * to generate BE's root dataset string.
 465                  */
 466                 if ((root_ds = be_get_ds_from_dir("/")) != NULL) {
 467                         (void) snprintf(be_root_ds, be_root_ds_size, "%s/%s",
 468                             dirname(root_ds), be_name);
 469                 } else {
 470                         be_print_err(gettext("be_make_root_ds: zone root "
 471                             "dataset is not mounted\n"));
 472                         return;
 473                 }
 474         }
 475 }
 476 
 477 /*
 478  * Function:    be_make_container_ds
 479  * Description: Generate string for the BE container dataset given a pool name.
 480  * Parameters:
 481  *              zpool - pointer zpool name.
 482  *              container_ds - pointer to buffer to return BE container
 483  *                      dataset in.
 484  *              container_ds_size - size of container_ds
 485  * Returns:
 486  *              None
 487  * Scope:
 488  *              Semi-private (library wide use only)
 489  */
 490 void
 491 be_make_container_ds(const char *zpool,  char *container_ds,
 492     int container_ds_size)
 493 {
 494         struct be_defaults be_defaults;
 495         be_get_defaults(&be_defaults);
 496         char    *root_ds = NULL;
 497 
 498         if (getzoneid() == GLOBAL_ZONEID) {
 499                 if (be_defaults.be_deflt_rpool_container) {
 500                         (void) snprintf(container_ds, container_ds_size,
 501                             "%s", zpool);
 502                 } else {
 503                         (void) snprintf(container_ds, container_ds_size,
 504                             "%s/%s", zpool, BE_CONTAINER_DS_NAME);
 505                 }
 506         } else {
 507                 if ((root_ds = be_get_ds_from_dir("/")) != NULL) {
 508                         (void) strlcpy(container_ds, dirname(root_ds),
 509                             container_ds_size);
 510                 } else {
 511                         be_print_err(gettext("be_make_container_ds: zone root "
 512                             "dataset is not mounted\n"));
 513                         return;
 514                 }
 515         }
 516 }
 517 
 518 /*
 519  * Function:    be_make_name_from_ds
 520  * Description: This function takes a dataset name and strips off the
 521  *              BE container dataset portion from the beginning.  The
 522  *              returned name is allocated in heap storage, so the caller
 523  *              is responsible for freeing it.
 524  * Parameters:
 525  *              dataset - dataset to get name from.
 526  *              rc_loc - dataset underwhich the root container dataset lives.
 527  * Returns:
 528  *              name of dataset relative to BE container dataset.
 529  *              NULL if dataset is not under a BE root dataset.
 530  * Scope:
 531  *              Semi-primate (library wide use only)
 532  */
 533 char *
 534 be_make_name_from_ds(const char *dataset, char *rc_loc)
 535 {
 536         char    ds[ZFS_MAX_DATASET_NAME_LEN];
 537         char    *tok = NULL;
 538         char    *name = NULL;
 539         struct be_defaults be_defaults;
 540         int     rlen = strlen(rc_loc);
 541 
 542         be_get_defaults(&be_defaults);
 543 
 544         /*
 545          * First token is the location of where the root container dataset
 546          * lives; it must match rc_loc.
 547          */
 548         if (strncmp(dataset, rc_loc, rlen) == 0 && dataset[rlen] == '/')
 549                 (void) strlcpy(ds, dataset + rlen + 1, sizeof (ds));
 550         else
 551                 return (NULL);
 552 
 553         if (be_defaults.be_deflt_rpool_container) {
 554                 if ((name = strdup(ds)) == NULL) {
 555                         be_print_err(gettext("be_make_name_from_ds: "
 556                             "memory allocation failed\n"));
 557                         return (NULL);
 558                 }
 559         } else {
 560                 /* Second token must be BE container dataset name */
 561                 if ((tok = strtok(ds, "/")) == NULL ||
 562                     strcmp(tok, BE_CONTAINER_DS_NAME) != 0)
 563                         return (NULL);
 564 
 565                 /* Return the remaining token if one exists */
 566                 if ((tok = strtok(NULL, "")) == NULL)
 567                         return (NULL);
 568 
 569                 if ((name = strdup(tok)) == NULL) {
 570                         be_print_err(gettext("be_make_name_from_ds: "
 571                             "memory allocation failed\n"));
 572                         return (NULL);
 573                 }
 574         }
 575 
 576         return (name);
 577 }
 578 
 579 /*
 580  * Function:    be_maxsize_avail
 581  * Description: Returns the available size for the zfs handle passed in.
 582  * Parameters:
 583  *              zhp - A pointer to the open zfs handle.
 584  *              ret - The available size will be returned in this.
 585  * Returns:
 586  *              The error returned by the zfs get property function.
 587  * Scope:
 588  *              Semi-private (library wide use only)
 589  */
 590 int
 591 be_maxsize_avail(zfs_handle_t *zhp, uint64_t *ret)
 592 {
 593         return ((*ret = zfs_prop_get_int(zhp, ZFS_PROP_AVAILABLE)));
 594 }
 595 
 596 /*
 597  * Function:    be_append_menu
 598  * Description: Appends an entry for a BE into the menu.lst.
 599  * Parameters:
 600  *              be_name - pointer to name of BE to add boot menu entry for.
 601  *              be_root_pool - pointer to name of pool BE lives in.
 602  *              boot_pool - Used if the pool containing the grub menu is
 603  *                          different than the one contaiing the BE. This
 604  *                          will normally be NULL.
 605  *              be_orig_root_ds - The root dataset for the BE. This is
 606  *                      used to check to see if an entry already exists
 607  *                      for this BE.
 608  *              description - pointer to description of BE to be added in
 609  *                      the title line for this BEs entry.
 610  * Returns:
 611  *              BE_SUCCESS - Success
 612  *              be_errno_t - Failure
 613  * Scope:
 614  *              Semi-private (library wide use only)
 615  */
 616 int
 617 be_append_menu(char *be_name, char *be_root_pool, char *boot_pool,
 618     char *be_orig_root_ds, char *description)
 619 {
 620         zfs_handle_t *zhp = NULL;
 621         char menu_file[MAXPATHLEN];
 622         char be_root_ds[MAXPATHLEN];
 623         char line[BUFSIZ];
 624         char temp_line[BUFSIZ];
 625         char title[MAXPATHLEN];
 626         char *entries[BUFSIZ];
 627         char *tmp_entries[BUFSIZ];
 628         char *pool_mntpnt = NULL;
 629         char *ptmp_mntpnt = NULL;
 630         char *orig_mntpnt = NULL;
 631         boolean_t found_be = B_FALSE;
 632         boolean_t found_orig_be = B_FALSE;
 633         boolean_t found_title = B_FALSE;
 634         boolean_t pool_mounted = B_FALSE;
 635         boolean_t collect_lines = B_FALSE;
 636         FILE *menu_fp = NULL;
 637         int err = 0, ret = BE_SUCCESS;
 638         int i, num_tmp_lines = 0, num_lines = 0;
 639 
 640         if (be_name == NULL || be_root_pool == NULL)
 641                 return (BE_ERR_INVAL);
 642 
 643         if (boot_pool == NULL)
 644                 boot_pool = be_root_pool;
 645 
 646         if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
 647                 be_print_err(gettext("be_append_menu: failed to open "
 648                     "pool dataset for %s: %s\n"), be_root_pool,
 649                     libzfs_error_description(g_zfs));
 650                 return (zfs_err_to_be_err(g_zfs));
 651         }
 652 
 653         /*
 654          * Check to see if the pool's dataset is mounted. If it isn't we'll
 655          * attempt to mount it.
 656          */
 657         if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
 658             &pool_mounted)) != BE_SUCCESS) {
 659                 be_print_err(gettext("be_append_menu: pool dataset "
 660                     "(%s) could not be mounted\n"), be_root_pool);
 661                 ZFS_CLOSE(zhp);
 662                 return (ret);
 663         }
 664 
 665         /*
 666          * Get the mountpoint for the root pool dataset.
 667          */
 668         if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
 669                 be_print_err(gettext("be_append_menu: pool "
 670                     "dataset (%s) is not mounted. Can't set "
 671                     "the default BE in the grub menu.\n"), be_root_pool);
 672                 ret = BE_ERR_NO_MENU;
 673                 goto cleanup;
 674         }
 675 
 676         /*
 677          * Check to see if this system supports grub
 678          */
 679         if (be_has_grub()) {
 680                 (void) snprintf(menu_file, sizeof (menu_file),
 681                     "%s%s", pool_mntpnt, BE_GRUB_MENU);
 682         } else {
 683                 (void) snprintf(menu_file, sizeof (menu_file),
 684                     "%s%s", pool_mntpnt, BE_SPARC_MENU);
 685         }
 686 
 687         be_make_root_ds(be_root_pool, be_name, be_root_ds, sizeof (be_root_ds));
 688 
 689         /*
 690          * Iterate through menu first to make sure the BE doesn't already
 691          * have an entry in the menu.
 692          *
 693          * Additionally while iterating through the menu, if we have an
 694          * original root dataset for a BE we're cloning from, we need to keep
 695          * track of that BE's menu entry. We will then use the lines from
 696          * that entry to create the entry for the new BE.
 697          */
 698         if ((ret = be_open_menu(be_root_pool, menu_file,
 699             &menu_fp, "r", B_TRUE)) != BE_SUCCESS) {
 700                 goto cleanup;
 701         } else if (menu_fp == NULL) {
 702                 ret = BE_ERR_NO_MENU;
 703                 goto cleanup;
 704         }
 705 
 706         free(pool_mntpnt);
 707         pool_mntpnt = NULL;
 708 
 709         while (fgets(line, BUFSIZ, menu_fp)) {
 710                 char *tok = NULL;
 711 
 712                 (void) strlcpy(temp_line, line, BUFSIZ);
 713                 tok = strtok(line, BE_WHITE_SPACE);
 714 
 715                 if (tok == NULL || tok[0] == '#') {
 716                         continue;
 717                 } else if (strcmp(tok, "title") == 0) {
 718                         collect_lines = B_FALSE;
 719                         if ((tok = strtok(NULL, "\n")) == NULL)
 720                                 (void) strlcpy(title, "", sizeof (title));
 721                         else
 722                                 (void) strlcpy(title, tok, sizeof (title));
 723                         found_title = B_TRUE;
 724 
 725                         if (num_tmp_lines != 0) {
 726                                 for (i = 0; i < num_tmp_lines; i++) {
 727                                         free(tmp_entries[i]);
 728                                         tmp_entries[i] = NULL;
 729                                 }
 730                                 num_tmp_lines = 0;
 731                         }
 732                 } else if (strcmp(tok, "bootfs") == 0) {
 733                         char *bootfs = strtok(NULL, BE_WHITE_SPACE);
 734                         found_title = B_FALSE;
 735                         if (bootfs == NULL)
 736                                 continue;
 737 
 738                         if (strcmp(bootfs, be_root_ds) == 0) {
 739                                 found_be = B_TRUE;
 740                                 break;
 741                         }
 742 
 743                         if (be_orig_root_ds != NULL &&
 744                             strcmp(bootfs, be_orig_root_ds) == 0 &&
 745                             !found_orig_be) {
 746                                 char str[BUFSIZ];
 747                                 found_orig_be = B_TRUE;
 748                                 num_lines = 0;
 749                                 /*
 750                                  * Store the new title line
 751                                  */
 752                                 (void) snprintf(str, BUFSIZ, "title %s\n",
 753                                     description ? description : be_name);
 754                                 entries[num_lines] = strdup(str);
 755                                 num_lines++;
 756                                 /*
 757                                  * If there are any lines between the title
 758                                  * and the bootfs line store these. Also
 759                                  * free the temporary lines.
 760                                  */
 761                                 for (i = 0; i < num_tmp_lines; i++) {
 762                                         entries[num_lines] = tmp_entries[i];
 763                                         tmp_entries[i] = NULL;
 764                                         num_lines++;
 765                                 }
 766                                 num_tmp_lines = 0;
 767                                 /*
 768                                  * Store the new bootfs line.
 769                                  */
 770                                 (void) snprintf(str, BUFSIZ, "bootfs %s\n",
 771                                     be_root_ds);
 772                                 entries[num_lines] = strdup(str);
 773                                 num_lines++;
 774                                 collect_lines = B_TRUE;
 775                         }
 776                 } else if (found_orig_be && collect_lines) {
 777                         /*
 778                          * get the rest of the lines for the original BE and
 779                          * store them.
 780                          */
 781                         if (strstr(line, BE_GRUB_COMMENT) != NULL ||
 782                             strstr(line, "BOOTADM") != NULL)
 783                                 continue;
 784                         if (strcmp(tok, "splashimage") == 0) {
 785                                 entries[num_lines] =
 786                                     strdup("splashimage "
 787                                     "/boot/splashimage.xpm\n");
 788                         } else {
 789                                 entries[num_lines] = strdup(temp_line);
 790                         }
 791                         num_lines++;
 792                 } else if (found_title && !found_orig_be) {
 793                         tmp_entries[num_tmp_lines] = strdup(temp_line);
 794                         num_tmp_lines++;
 795                 }
 796         }
 797 
 798         (void) fclose(menu_fp);
 799 
 800         if (found_be) {
 801                 /*
 802                  * If an entry for this BE was already in the menu, then if
 803                  * that entry's title matches what we would have put in
 804                  * return success.  Otherwise return failure.
 805                  */
 806                 char *new_title = description ? description : be_name;
 807 
 808                 if (strcmp(title, new_title) == 0) {
 809                         ret = BE_SUCCESS;
 810                         goto cleanup;
 811                 } else {
 812                         if (be_remove_menu(be_name, be_root_pool,
 813                             boot_pool) != BE_SUCCESS) {
 814                                 be_print_err(gettext("be_append_menu: "
 815                                     "Failed to remove existing unusable "
 816                                     "entry '%s' in boot menu.\n"), be_name);
 817                                 ret = BE_ERR_BE_EXISTS;
 818                                 goto cleanup;
 819                         }
 820                 }
 821         }
 822 
 823         /* Append BE entry to the end of the file */
 824         menu_fp = fopen(menu_file, "a+");
 825         err = errno;
 826         if (menu_fp == NULL) {
 827                 be_print_err(gettext("be_append_menu: failed "
 828                     "to open menu.lst file %s\n"), menu_file);
 829                 ret = errno_to_be_err(err);
 830                 goto cleanup;
 831         }
 832 
 833         if (found_orig_be) {
 834                 /*
 835                  * write out all the stored lines
 836                  */
 837                 for (i = 0; i < num_lines; i++) {
 838                         (void) fprintf(menu_fp, "%s", entries[i]);
 839                         free(entries[i]);
 840                 }
 841                 num_lines = 0;
 842 
 843                 /*
 844                  * Check to see if this system supports grub
 845                  */
 846                 if (be_has_grub())
 847                         (void) fprintf(menu_fp, "%s\n", BE_GRUB_COMMENT);
 848                 ret = BE_SUCCESS;
 849         } else {
 850                 (void) fprintf(menu_fp, "title %s\n",
 851                     description ? description : be_name);
 852                 (void) fprintf(menu_fp, "bootfs %s\n", be_root_ds);
 853 
 854                 /*
 855                  * Check to see if this system supports grub
 856                  */
 857                 if (be_has_grub()) {
 858                         (void) fprintf(menu_fp, "kernel$ "
 859                             "/platform/i86pc/kernel/$ISADIR/unix -B "
 860                             "$ZFS-BOOTFS\n");
 861                         (void) fprintf(menu_fp, "module$ "
 862                             "/platform/i86pc/$ISADIR/boot_archive\n");
 863                         (void) fprintf(menu_fp, "%s\n", BE_GRUB_COMMENT);
 864                 }
 865                 ret = BE_SUCCESS;
 866         }
 867         (void) fclose(menu_fp);
 868 cleanup:
 869         if (pool_mounted) {
 870                 int err = BE_SUCCESS;
 871                 err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
 872                 if (ret == BE_SUCCESS)
 873                         ret = err;
 874                 free(orig_mntpnt);
 875                 free(ptmp_mntpnt);
 876         }
 877         ZFS_CLOSE(zhp);
 878         if (num_tmp_lines > 0) {
 879                 for (i = 0; i < num_tmp_lines; i++) {
 880                         free(tmp_entries[i]);
 881                         tmp_entries[i] = NULL;
 882                 }
 883         }
 884         if (num_lines > 0) {
 885                 for (i = 0; i < num_lines; i++) {
 886                         free(entries[i]);
 887                         entries[i] = NULL;
 888                 }
 889         }
 890         return (ret);
 891 }
 892 
 893 /*
 894  * Function:    be_remove_menu
 895  * Description: Removes a BE's entry from a menu.lst file.
 896  * Parameters:
 897  *              be_name - the name of BE whose entry is to be removed from
 898  *                      the menu.lst file.
 899  *              be_root_pool - the pool that be_name lives in.
 900  *              boot_pool - the pool where the BE is, if different than
 901  *                      the pool containing the boot menu.  If this is
 902  *                      NULL it will be set to be_root_pool.
 903  * Returns:
 904  *              BE_SUCCESS - Success
 905  *              be_errno_t - Failure
 906  * Scope:
 907  *              Semi-private (library wide use only)
 908  */
 909 int
 910 be_remove_menu(char *be_name, char *be_root_pool, char *boot_pool)
 911 {
 912         zfs_handle_t    *zhp = NULL;
 913         char            be_root_ds[MAXPATHLEN];
 914         char            **buffer = NULL;
 915         char            menu_buf[BUFSIZ];
 916         char            menu[MAXPATHLEN];
 917         char            *pool_mntpnt = NULL;
 918         char            *ptmp_mntpnt = NULL;
 919         char            *orig_mntpnt = NULL;
 920         char            *tmp_menu = NULL;
 921         FILE            *menu_fp = NULL;
 922         FILE            *tmp_menu_fp = NULL;
 923         struct stat     sb;
 924         int             ret = BE_SUCCESS;
 925         int             i;
 926         int             fd;
 927         int             err = 0;
 928         int             nlines = 0;
 929         int             default_entry = 0;
 930         int             entry_cnt = 0;
 931         int             entry_del = 0;
 932         int             num_entry_del = 0;
 933         int             tmp_menu_len = 0;
 934         boolean_t       write = B_TRUE;
 935         boolean_t       do_buffer = B_FALSE;
 936         boolean_t       pool_mounted = B_FALSE;
 937 
 938         if (boot_pool == NULL)
 939                 boot_pool = be_root_pool;
 940 
 941         /* Get name of BE's root dataset */
 942         be_make_root_ds(be_root_pool, be_name, be_root_ds, sizeof (be_root_ds));
 943 
 944         /* Get handle to pool dataset */
 945         if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
 946                 be_print_err(gettext("be_remove_menu: "
 947                     "failed to open pool dataset for %s: %s"),
 948                     be_root_pool, libzfs_error_description(g_zfs));
 949                 return (zfs_err_to_be_err(g_zfs));
 950         }
 951 
 952         /*
 953          * Check to see if the pool's dataset is mounted. If it isn't we'll
 954          * attempt to mount it.
 955          */
 956         if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
 957             &pool_mounted)) != BE_SUCCESS) {
 958                 be_print_err(gettext("be_remove_menu: pool dataset "
 959                     "(%s) could not be mounted\n"), be_root_pool);
 960                 ZFS_CLOSE(zhp);
 961                 return (ret);
 962         }
 963 
 964         /*
 965          * Get the mountpoint for the root pool dataset.
 966          */
 967         if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
 968                 be_print_err(gettext("be_remove_menu: pool "
 969                     "dataset (%s) is not mounted. Can't set "
 970                     "the default BE in the grub menu.\n"), be_root_pool);
 971                 ret = BE_ERR_NO_MENU;
 972                 goto cleanup;
 973         }
 974 
 975         /* Get path to boot menu */
 976         (void) strlcpy(menu, pool_mntpnt, sizeof (menu));
 977 
 978         /*
 979          * Check to see if this system supports grub
 980          */
 981         if (be_has_grub())
 982                 (void) strlcat(menu, BE_GRUB_MENU, sizeof (menu));
 983         else
 984                 (void) strlcat(menu, BE_SPARC_MENU, sizeof (menu));
 985 
 986         /* Get handle to boot menu file */
 987         if ((ret = be_open_menu(be_root_pool, menu, &menu_fp, "r",
 988             B_TRUE)) != BE_SUCCESS) {
 989                 goto cleanup;
 990         } else if (menu_fp == NULL) {
 991                 ret = BE_ERR_NO_MENU;
 992                 goto cleanup;
 993         }
 994 
 995         free(pool_mntpnt);
 996         pool_mntpnt = NULL;
 997 
 998         /* Grab the stats of the original menu file */
 999         if (stat(menu, &sb) != 0) {
1000                 err = errno;
1001                 be_print_err(gettext("be_remove_menu: "
1002                     "failed to stat file %s: %s\n"), menu, strerror(err));
1003                 ret = errno_to_be_err(err);
1004                 goto cleanup;
1005         }
1006 
1007         /* Create a tmp file for the modified menu.lst */
1008         tmp_menu_len = strlen(menu) + 7;
1009         if ((tmp_menu = (char *)malloc(tmp_menu_len)) == NULL) {
1010                 be_print_err(gettext("be_remove_menu: malloc failed\n"));
1011                 ret = BE_ERR_NOMEM;
1012                 goto cleanup;
1013         }
1014         (void) memset(tmp_menu, 0, tmp_menu_len);
1015         (void) strlcpy(tmp_menu, menu, tmp_menu_len);
1016         (void) strlcat(tmp_menu, "XXXXXX", tmp_menu_len);
1017         if ((fd = mkstemp(tmp_menu)) == -1) {
1018                 err = errno;
1019                 be_print_err(gettext("be_remove_menu: mkstemp failed\n"));
1020                 ret = errno_to_be_err(err);
1021                 free(tmp_menu);
1022                 tmp_menu = NULL;
1023                 goto cleanup;
1024         }
1025         if ((tmp_menu_fp = fdopen(fd, "w")) == NULL) {
1026                 err = errno;
1027                 be_print_err(gettext("be_remove_menu: "
1028                     "could not open tmp file for write: %s\n"), strerror(err));
1029                 (void) close(fd);
1030                 ret = errno_to_be_err(err);
1031                 goto cleanup;
1032         }
1033 
1034         while (fgets(menu_buf, BUFSIZ, menu_fp)) {
1035                 char tline [BUFSIZ];
1036                 char *tok = NULL;
1037 
1038                 (void) strlcpy(tline, menu_buf, sizeof (tline));
1039 
1040                 /* Tokenize line */
1041                 tok = strtok(tline, BE_WHITE_SPACE);
1042 
1043                 if (tok == NULL || tok[0] == '#') {
1044                         /* Found empty line or comment line */
1045                         if (do_buffer) {
1046                                 /* Buffer this line */
1047                                 if ((buffer = (char **)realloc(buffer,
1048                                     sizeof (char *)*(nlines + 1))) == NULL) {
1049                                         ret = BE_ERR_NOMEM;
1050                                         goto cleanup;
1051                                 }
1052                                 if ((buffer[nlines++] = strdup(menu_buf))
1053                                     == NULL) {
1054                                         ret = BE_ERR_NOMEM;
1055                                         goto cleanup;
1056                                 }
1057 
1058                         } else if (write || strncmp(menu_buf, BE_GRUB_COMMENT,
1059                             strlen(BE_GRUB_COMMENT)) != 0) {
1060                                 /* Write this line out */
1061                                 (void) fputs(menu_buf, tmp_menu_fp);
1062                         }
1063                 } else if (strcmp(tok, "default") == 0) {
1064                         /*
1065                          * Record what 'default' is set to because we might
1066                          * need to adjust this upon deleting an entry.
1067                          */
1068                         tok = strtok(NULL, BE_WHITE_SPACE);
1069 
1070                         if (tok != NULL) {
1071                                 default_entry = atoi(tok);
1072                         }
1073 
1074                         (void) fputs(menu_buf, tmp_menu_fp);
1075                 } else if (strcmp(tok, "title") == 0) {
1076                         /*
1077                          * If we've reached a 'title' line and do_buffer is
1078                          * is true, that means we've just buffered an entire
1079                          * entry without finding a 'bootfs' directive.  We
1080                          * need to write that entry out and keep searching.
1081                          */
1082                         if (do_buffer) {
1083                                 for (i = 0; i < nlines; i++) {
1084                                         (void) fputs(buffer[i], tmp_menu_fp);
1085                                         free(buffer[i]);
1086                                 }
1087                                 free(buffer);
1088                                 buffer = NULL;
1089                                 nlines = 0;
1090                         }
1091 
1092                         /*
1093                          * Turn writing off and buffering on, and increment
1094                          * our entry counter.
1095                          */
1096                         write = B_FALSE;
1097                         do_buffer = B_TRUE;
1098                         entry_cnt++;
1099 
1100                         /* Buffer this 'title' line */
1101                         if ((buffer = (char **)realloc(buffer,
1102                             sizeof (char *)*(nlines + 1))) == NULL) {
1103                                 ret = BE_ERR_NOMEM;
1104                                 goto cleanup;
1105                         }
1106                         if ((buffer[nlines++] = strdup(menu_buf)) == NULL) {
1107                                 ret = BE_ERR_NOMEM;
1108                                 goto cleanup;
1109                         }
1110 
1111                 } else if (strcmp(tok, "bootfs") == 0) {
1112                         char *bootfs = NULL;
1113 
1114                         /*
1115                          * Found a 'bootfs' line.  See if it matches the
1116                          * BE we're looking for.
1117                          */
1118                         if ((bootfs = strtok(NULL, BE_WHITE_SPACE)) == NULL ||
1119                             strcmp(bootfs, be_root_ds) != 0) {
1120                                 /*
1121                                  * Either there's nothing after the 'bootfs'
1122                                  * or this is not the BE we're looking for,
1123                                  * write out the line(s) we've buffered since
1124                                  * finding the title.
1125                                  */
1126                                 for (i = 0; i < nlines; i++) {
1127                                         (void) fputs(buffer[i], tmp_menu_fp);
1128                                         free(buffer[i]);
1129                                 }
1130                                 free(buffer);
1131                                 buffer = NULL;
1132                                 nlines = 0;
1133 
1134                                 /*
1135                                  * Turn writing back on, and turn off buffering
1136                                  * since this isn't the entry we're looking
1137                                  * for.
1138                                  */
1139                                 write = B_TRUE;
1140                                 do_buffer = B_FALSE;
1141 
1142                                 /* Write this 'bootfs' line out. */
1143                                 (void) fputs(menu_buf, tmp_menu_fp);
1144                         } else {
1145                                 /*
1146                                  * Found the entry we're looking for.
1147                                  * Record its entry number, increment the
1148                                  * number of entries we've deleted, and turn
1149                                  * writing off.  Also, throw away the lines
1150                                  * we've buffered for this entry so far, we
1151                                  * don't need them.
1152                                  */
1153                                 entry_del = entry_cnt - 1;
1154                                 num_entry_del++;
1155                                 write = B_FALSE;
1156                                 do_buffer = B_FALSE;
1157 
1158                                 for (i = 0; i < nlines; i++) {
1159                                         free(buffer[i]);
1160                                 }
1161                                 free(buffer);
1162                                 buffer = NULL;
1163                                 nlines = 0;
1164                         }
1165                 } else {
1166                         if (do_buffer) {
1167                                 /* Buffer this line */
1168                                 if ((buffer = (char **)realloc(buffer,
1169                                     sizeof (char *)*(nlines + 1))) == NULL) {
1170                                         ret = BE_ERR_NOMEM;
1171                                         goto cleanup;
1172                                 }
1173                                 if ((buffer[nlines++] = strdup(menu_buf))
1174                                     == NULL) {
1175                                         ret = BE_ERR_NOMEM;
1176                                         goto cleanup;
1177                                 }
1178                         } else if (write) {
1179                                 /* Write this line out */
1180                                 (void) fputs(menu_buf, tmp_menu_fp);
1181                         }
1182                 }
1183         }
1184 
1185         (void) fclose(menu_fp);
1186         menu_fp = NULL;
1187         (void) fclose(tmp_menu_fp);
1188         tmp_menu_fp = NULL;
1189 
1190         /* Copy the modified menu.lst into place */
1191         if (rename(tmp_menu, menu) != 0) {
1192                 err = errno;
1193                 be_print_err(gettext("be_remove_menu: "
1194                     "failed to rename file %s to %s: %s\n"),
1195                     tmp_menu, menu, strerror(err));
1196                 ret = errno_to_be_err(err);
1197                 goto cleanup;
1198         }
1199         free(tmp_menu);
1200         tmp_menu = NULL;
1201 
1202         /*
1203          * If we've removed an entry, see if we need to
1204          * adjust the default value in the menu.lst.  If the
1205          * entry we've deleted comes before the default entry
1206          * we need to adjust the default value accordingly.
1207          *
1208          * be_has_grub is used here to check to see if this system
1209          * supports grub.
1210          */
1211         if (be_has_grub() && num_entry_del > 0) {
1212                 if (entry_del <= default_entry) {
1213                         default_entry = default_entry - num_entry_del;
1214                         if (default_entry < 0)
1215                                 default_entry = 0;
1216 
1217                         /*
1218                          * Adjust the default value by rewriting the
1219                          * menu.lst file.  This may be overkill, but to
1220                          * preserve the location of the 'default' entry
1221                          * in the file, we need to do this.
1222                          */
1223 
1224                         /* Get handle to boot menu file */
1225                         if ((menu_fp = fopen(menu, "r")) == NULL) {
1226                                 err = errno;
1227                                 be_print_err(gettext("be_remove_menu: "
1228                                     "failed to open menu.lst (%s): %s\n"),
1229                                     menu, strerror(err));
1230                                 ret = errno_to_be_err(err);
1231                                 goto cleanup;
1232                         }
1233 
1234                         /* Create a tmp file for the modified menu.lst */
1235                         tmp_menu_len = strlen(menu) + 7;
1236                         if ((tmp_menu = (char *)malloc(tmp_menu_len))
1237                             == NULL) {
1238                                 be_print_err(gettext("be_remove_menu: "
1239                                     "malloc failed\n"));
1240                                 ret = BE_ERR_NOMEM;
1241                                 goto cleanup;
1242                         }
1243                         (void) memset(tmp_menu, 0, tmp_menu_len);
1244                         (void) strlcpy(tmp_menu, menu, tmp_menu_len);
1245                         (void) strlcat(tmp_menu, "XXXXXX", tmp_menu_len);
1246                         if ((fd = mkstemp(tmp_menu)) == -1) {
1247                                 err = errno;
1248                                 be_print_err(gettext("be_remove_menu: "
1249                                     "mkstemp failed: %s\n"), strerror(err));
1250                                 ret = errno_to_be_err(err);
1251                                 free(tmp_menu);
1252                                 tmp_menu = NULL;
1253                                 goto cleanup;
1254                         }
1255                         if ((tmp_menu_fp = fdopen(fd, "w")) == NULL) {
1256                                 err = errno;
1257                                 be_print_err(gettext("be_remove_menu: "
1258                                     "could not open tmp file for write: %s\n"),
1259                                     strerror(err));
1260                                 (void) close(fd);
1261                                 ret = errno_to_be_err(err);
1262                                 goto cleanup;
1263                         }
1264 
1265                         while (fgets(menu_buf, BUFSIZ, menu_fp)) {
1266                                 char tline [BUFSIZ];
1267                                 char *tok = NULL;
1268 
1269                                 (void) strlcpy(tline, menu_buf, sizeof (tline));
1270 
1271                                 /* Tokenize line */
1272                                 tok = strtok(tline, BE_WHITE_SPACE);
1273 
1274                                 if (tok == NULL) {
1275                                         /* Found empty line, write it out */
1276                                         (void) fputs(menu_buf, tmp_menu_fp);
1277                                 } else if (strcmp(tok, "default") == 0) {
1278                                         /* Found the default line, adjust it */
1279                                         (void) snprintf(tline, sizeof (tline),
1280                                             "default %d\n", default_entry);
1281 
1282                                         (void) fputs(tline, tmp_menu_fp);
1283                                 } else {
1284                                         /* Pass through all other lines */
1285                                         (void) fputs(menu_buf, tmp_menu_fp);
1286                                 }
1287                         }
1288 
1289                         (void) fclose(menu_fp);
1290                         menu_fp = NULL;
1291                         (void) fclose(tmp_menu_fp);
1292                         tmp_menu_fp = NULL;
1293 
1294                         /* Copy the modified menu.lst into place */
1295                         if (rename(tmp_menu, menu) != 0) {
1296                                 err = errno;
1297                                 be_print_err(gettext("be_remove_menu: "
1298                                     "failed to rename file %s to %s: %s\n"),
1299                                     tmp_menu, menu, strerror(err));
1300                                 ret = errno_to_be_err(err);
1301                                 goto cleanup;
1302                         }
1303 
1304                         free(tmp_menu);
1305                         tmp_menu = NULL;
1306                 }
1307         }
1308 
1309         /* Set the perms and ownership of the updated file */
1310         if (chmod(menu, sb.st_mode) != 0) {
1311                 err = errno;
1312                 be_print_err(gettext("be_remove_menu: "
1313                     "failed to chmod %s: %s\n"), menu, strerror(err));
1314                 ret = errno_to_be_err(err);
1315                 goto cleanup;
1316         }
1317         if (chown(menu, sb.st_uid, sb.st_gid) != 0) {
1318                 err = errno;
1319                 be_print_err(gettext("be_remove_menu: "
1320                     "failed to chown %s: %s\n"), menu, strerror(err));
1321                 ret = errno_to_be_err(err);
1322                 goto cleanup;
1323         }
1324 
1325 cleanup:
1326         if (pool_mounted) {
1327                 int err = BE_SUCCESS;
1328                 err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
1329                 if (ret == BE_SUCCESS)
1330                         ret = err;
1331                 free(orig_mntpnt);
1332                 free(ptmp_mntpnt);
1333         }
1334         ZFS_CLOSE(zhp);
1335 
1336         free(buffer);
1337         if (menu_fp != NULL)
1338                 (void) fclose(menu_fp);
1339         if (tmp_menu_fp != NULL)
1340                 (void) fclose(tmp_menu_fp);
1341         if (tmp_menu != NULL) {
1342                 (void) unlink(tmp_menu);
1343                 free(tmp_menu);
1344         }
1345 
1346         return (ret);
1347 }
1348 
1349 /*
1350  * Function:    be_default_grub_bootfs
1351  * Description: This function returns the dataset in the default entry of
1352  *              the grub menu. If no default entry is found with a valid bootfs
1353  *              entry NULL is returned.
1354  * Parameters:
1355  *              be_root_pool - This is the name of the root pool where the
1356  *                             grub menu can be found.
1357  *              def_bootfs - This is used to pass back the bootfs string. On
1358  *                              error NULL is returned here.
1359  * Returns:
1360  *              Success - BE_SUCCESS is returned.
1361  *              Failure - a be_errno_t is returned.
1362  * Scope:
1363  *              Semi-private (library wide use only)
1364  */
1365 int
1366 be_default_grub_bootfs(const char *be_root_pool, char **def_bootfs)
1367 {
1368         zfs_handle_t    *zhp = NULL;
1369         char            grub_file[MAXPATHLEN];
1370         FILE            *menu_fp;
1371         char            line[BUFSIZ];
1372         char            *pool_mntpnt = NULL;
1373         char            *ptmp_mntpnt = NULL;
1374         char            *orig_mntpnt = NULL;
1375         int             default_entry = 0, entries = 0;
1376         int             found_default = 0;
1377         int             ret = BE_SUCCESS;
1378         boolean_t       pool_mounted = B_FALSE;
1379 
1380         errno = 0;
1381 
1382         /*
1383          * Check to see if this system supports grub
1384          */
1385         if (!be_has_grub()) {
1386                 be_print_err(gettext("be_default_grub_bootfs: operation "
1387                     "not supported on this architecture\n"));
1388                 return (BE_ERR_NOTSUP);
1389         }
1390 
1391         *def_bootfs = NULL;
1392 
1393         /* Get handle to pool dataset */
1394         if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
1395                 be_print_err(gettext("be_default_grub_bootfs: "
1396                     "failed to open pool dataset for %s: %s"),
1397                     be_root_pool, libzfs_error_description(g_zfs));
1398                 return (zfs_err_to_be_err(g_zfs));
1399         }
1400 
1401         /*
1402          * Check to see if the pool's dataset is mounted. If it isn't we'll
1403          * attempt to mount it.
1404          */
1405         if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
1406             &pool_mounted)) != BE_SUCCESS) {
1407                 be_print_err(gettext("be_default_grub_bootfs: pool dataset "
1408                     "(%s) could not be mounted\n"), be_root_pool);
1409                 ZFS_CLOSE(zhp);
1410                 return (ret);
1411         }
1412 
1413         /*
1414          * Get the mountpoint for the root pool dataset.
1415          */
1416         if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
1417                 be_print_err(gettext("be_default_grub_bootfs: failed "
1418                     "to get mount point for the root pool. Can't set "
1419                     "the default BE in the grub menu.\n"));
1420                 ret = BE_ERR_NO_MENU;
1421                 goto cleanup;
1422         }
1423 
1424         (void) snprintf(grub_file, MAXPATHLEN, "%s%s",
1425             pool_mntpnt, BE_GRUB_MENU);
1426 
1427         if ((ret = be_open_menu((char *)be_root_pool, grub_file,
1428             &menu_fp, "r", B_FALSE)) != BE_SUCCESS) {
1429                 goto cleanup;
1430         } else if (menu_fp == NULL) {
1431                 ret = BE_ERR_NO_MENU;
1432                 goto cleanup;
1433         }
1434 
1435         free(pool_mntpnt);
1436         pool_mntpnt = NULL;
1437 
1438         while (fgets(line, BUFSIZ, menu_fp)) {
1439                 char *tok = strtok(line, BE_WHITE_SPACE);
1440 
1441                 if (tok != NULL && tok[0] != '#') {
1442                         if (!found_default) {
1443                                 if (strcmp(tok, "default") == 0) {
1444                                         tok = strtok(NULL, BE_WHITE_SPACE);
1445                                         if (tok != NULL) {
1446                                                 default_entry = atoi(tok);
1447                                                 rewind(menu_fp);
1448                                                 found_default = 1;
1449                                         }
1450                                 }
1451                                 continue;
1452                         }
1453                         if (strcmp(tok, "title") == 0) {
1454                                 entries++;
1455                         } else if (default_entry == entries - 1) {
1456                                 if (strcmp(tok, "bootfs") == 0) {
1457                                         tok = strtok(NULL, BE_WHITE_SPACE);
1458                                         (void) fclose(menu_fp);
1459 
1460                                         if (tok == NULL) {
1461                                                 ret = BE_SUCCESS;
1462                                                 goto cleanup;
1463                                         }
1464 
1465                                         if ((*def_bootfs = strdup(tok)) !=
1466                                             NULL) {
1467                                                 ret = BE_SUCCESS;
1468                                                 goto cleanup;
1469                                         }
1470                                         be_print_err(gettext(
1471                                             "be_default_grub_bootfs: "
1472                                             "memory allocation failed\n"));
1473                                         ret = BE_ERR_NOMEM;
1474                                         goto cleanup;
1475                                 }
1476                         } else if (default_entry < entries - 1) {
1477                                 /*
1478                                  * no bootfs entry for the default entry.
1479                                  */
1480                                 break;
1481                         }
1482                 }
1483         }
1484         (void) fclose(menu_fp);
1485 
1486 cleanup:
1487         if (pool_mounted) {
1488                 int err = BE_SUCCESS;
1489                 err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
1490                 if (ret == BE_SUCCESS)
1491                         ret = err;
1492                 free(orig_mntpnt);
1493                 free(ptmp_mntpnt);
1494         }
1495         ZFS_CLOSE(zhp);
1496         return (ret);
1497 }
1498 
1499 /*
1500  * Function:    be_change_grub_default
1501  * Description: This function takes two parameters. These are the name of
1502  *              the BE we want to have as the default booted in the grub
1503  *              menu and the root pool where the path to the grub menu exists.
1504  *              The code takes this and finds the BE's entry in the grub menu
1505  *              and changes the default entry to point to that entry in the
1506  *              list.
1507  * Parameters:
1508  *              be_name - This is the name of the BE wanted as the default
1509  *                      for the next boot.
1510  *              be_root_pool - This is the name of the root pool where the
1511  *                      grub menu can be found.
1512  * Returns:
1513  *              BE_SUCCESS - Success
1514  *              be_errno_t - Failure
1515  * Scope:
1516  *              Semi-private (library wide use only)
1517  */
1518 int
1519 be_change_grub_default(char *be_name, char *be_root_pool)
1520 {
1521         zfs_handle_t    *zhp = NULL;
1522         char    grub_file[MAXPATHLEN];
1523         char    *temp_grub = NULL;
1524         char    *pool_mntpnt = NULL;
1525         char    *ptmp_mntpnt = NULL;
1526         char    *orig_mntpnt = NULL;
1527         char    line[BUFSIZ];
1528         char    temp_line[BUFSIZ];
1529         char    be_root_ds[MAXPATHLEN];
1530         FILE    *grub_fp = NULL;
1531         FILE    *temp_fp = NULL;
1532         struct stat     sb;
1533         int     temp_grub_len = 0;
1534         int     fd, entries = 0;
1535         int     err = 0;
1536         int     ret = BE_SUCCESS;
1537         boolean_t       found_default = B_FALSE;
1538         boolean_t       pool_mounted = B_FALSE;
1539 
1540         errno = 0;
1541 
1542         /*
1543          * Check to see if this system supports grub
1544          */
1545         if (!be_has_grub()) {
1546                 be_print_err(gettext("be_change_grub_default: operation "
1547                     "not supported on this architecture\n"));
1548                 return (BE_ERR_NOTSUP);
1549         }
1550 
1551         /* Generate string for BE's root dataset */
1552         be_make_root_ds(be_root_pool, be_name, be_root_ds, sizeof (be_root_ds));
1553 
1554         /* Get handle to pool dataset */
1555         if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
1556                 be_print_err(gettext("be_change_grub_default: "
1557                     "failed to open pool dataset for %s: %s"),
1558                     be_root_pool, libzfs_error_description(g_zfs));
1559                 return (zfs_err_to_be_err(g_zfs));
1560         }
1561 
1562         /*
1563          * Check to see if the pool's dataset is mounted. If it isn't we'll
1564          * attempt to mount it.
1565          */
1566         if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
1567             &pool_mounted)) != BE_SUCCESS) {
1568                 be_print_err(gettext("be_change_grub_default: pool dataset "
1569                     "(%s) could not be mounted\n"), be_root_pool);
1570                 ZFS_CLOSE(zhp);
1571                 return (ret);
1572         }
1573 
1574         /*
1575          * Get the mountpoint for the root pool dataset.
1576          */
1577         if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
1578                 be_print_err(gettext("be_change_grub_default: pool "
1579                     "dataset (%s) is not mounted. Can't set "
1580                     "the default BE in the grub menu.\n"), be_root_pool);
1581                 ret = BE_ERR_NO_MENU;
1582                 goto cleanup;
1583         }
1584 
1585         (void) snprintf(grub_file, MAXPATHLEN, "%s%s",
1586             pool_mntpnt, BE_GRUB_MENU);
1587 
1588         if ((ret = be_open_menu(be_root_pool, grub_file,
1589             &grub_fp, "r+", B_TRUE)) != BE_SUCCESS) {
1590                 goto cleanup;
1591         } else if (grub_fp == NULL) {
1592                 ret = BE_ERR_NO_MENU;
1593                 goto cleanup;
1594         }
1595 
1596         free(pool_mntpnt);
1597         pool_mntpnt = NULL;
1598 
1599         /* Grab the stats of the original menu file */
1600         if (stat(grub_file, &sb) != 0) {
1601                 err = errno;
1602                 be_print_err(gettext("be_change_grub_default: "
1603                     "failed to stat file %s: %s\n"), grub_file, strerror(err));
1604                 ret = errno_to_be_err(err);
1605                 goto cleanup;
1606         }
1607 
1608         /* Create a tmp file for the modified menu.lst */
1609         temp_grub_len = strlen(grub_file) + 7;
1610         if ((temp_grub = (char *)malloc(temp_grub_len)) == NULL) {
1611                 be_print_err(gettext("be_change_grub_default: "
1612                     "malloc failed\n"));
1613                 ret = BE_ERR_NOMEM;
1614                 goto cleanup;
1615         }
1616         (void) memset(temp_grub, 0, temp_grub_len);
1617         (void) strlcpy(temp_grub, grub_file, temp_grub_len);
1618         (void) strlcat(temp_grub, "XXXXXX", temp_grub_len);
1619         if ((fd = mkstemp(temp_grub)) == -1) {
1620                 err = errno;
1621                 be_print_err(gettext("be_change_grub_default: "
1622                     "mkstemp failed: %s\n"), strerror(err));
1623                 ret = errno_to_be_err(err);
1624                 free(temp_grub);
1625                 temp_grub = NULL;
1626                 goto cleanup;
1627         }
1628         if ((temp_fp = fdopen(fd, "w")) == NULL) {
1629                 err = errno;
1630                 be_print_err(gettext("be_change_grub_default: "
1631                     "failed to open %s file: %s\n"),
1632                     temp_grub, strerror(err));
1633                 (void) close(fd);
1634                 ret = errno_to_be_err(err);
1635                 goto cleanup;
1636         }
1637 
1638         while (fgets(line, BUFSIZ, grub_fp)) {
1639                 char *tok = strtok(line, BE_WHITE_SPACE);
1640 
1641                 if (tok == NULL || tok[0] == '#') {
1642                         continue;
1643                 } else if (strcmp(tok, "title") == 0) {
1644                         entries++;
1645                         continue;
1646                 } else if (strcmp(tok, "bootfs") == 0) {
1647                         char *bootfs = strtok(NULL, BE_WHITE_SPACE);
1648                         if (bootfs == NULL)
1649                                 continue;
1650 
1651                         if (strcmp(bootfs, be_root_ds) == 0) {
1652                                 found_default = B_TRUE;
1653                                 break;
1654                         }
1655                 }
1656         }
1657 
1658         if (!found_default) {
1659                 be_print_err(gettext("be_change_grub_default: failed "
1660                     "to find entry for %s in the grub menu\n"),
1661                     be_name);
1662                 ret = BE_ERR_BE_NOENT;
1663                 goto cleanup;
1664         }
1665 
1666         rewind(grub_fp);
1667 
1668         while (fgets(line, BUFSIZ, grub_fp)) {
1669                 char *tok = NULL;
1670 
1671                 (void) strncpy(temp_line, line, BUFSIZ);
1672 
1673                 if ((tok = strtok(temp_line, BE_WHITE_SPACE)) != NULL &&
1674                     strcmp(tok, "default") == 0) {
1675                         (void) snprintf(temp_line, BUFSIZ, "default %d\n",
1676                             entries - 1 >= 0 ? entries - 1 : 0);
1677                         (void) fputs(temp_line, temp_fp);
1678                 } else {
1679                         (void) fputs(line, temp_fp);
1680                 }
1681         }
1682 
1683         (void) fclose(grub_fp);
1684         grub_fp = NULL;
1685         (void) fclose(temp_fp);
1686         temp_fp = NULL;
1687 
1688         if (rename(temp_grub, grub_file) != 0) {
1689                 err = errno;
1690                 be_print_err(gettext("be_change_grub_default: "
1691                     "failed to rename file %s to %s: %s\n"),
1692                     temp_grub, grub_file, strerror(err));
1693                 ret = errno_to_be_err(err);
1694                 goto cleanup;
1695         }
1696         free(temp_grub);
1697         temp_grub = NULL;
1698 
1699         /* Set the perms and ownership of the updated file */
1700         if (chmod(grub_file, sb.st_mode) != 0) {
1701                 err = errno;
1702                 be_print_err(gettext("be_change_grub_default: "
1703                     "failed to chmod %s: %s\n"), grub_file, strerror(err));
1704                 ret = errno_to_be_err(err);
1705                 goto cleanup;
1706         }
1707         if (chown(grub_file, sb.st_uid, sb.st_gid) != 0) {
1708                 err = errno;
1709                 be_print_err(gettext("be_change_grub_default: "
1710                     "failed to chown %s: %s\n"), grub_file, strerror(err));
1711                 ret = errno_to_be_err(err);
1712         }
1713 
1714 cleanup:
1715         if (pool_mounted) {
1716                 int err = BE_SUCCESS;
1717                 err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
1718                 if (ret == BE_SUCCESS)
1719                         ret = err;
1720                 free(orig_mntpnt);
1721                 free(ptmp_mntpnt);
1722         }
1723         ZFS_CLOSE(zhp);
1724         if (grub_fp != NULL)
1725                 (void) fclose(grub_fp);
1726         if (temp_fp != NULL)
1727                 (void) fclose(temp_fp);
1728         if (temp_grub != NULL) {
1729                 (void) unlink(temp_grub);
1730                 free(temp_grub);
1731         }
1732 
1733         return (ret);
1734 }
1735 
1736 /*
1737  * Function:    be_update_menu
1738  * Description: This function is used by be_rename to change the BE name in
1739  *              an existing entry in the grub menu to the new name of the BE.
1740  * Parameters:
1741  *              be_orig_name - the original name of the BE
1742  *              be_new_name - the new name the BE is being renameed to.
1743  *              be_root_pool - The pool which contains the grub menu
1744  *              boot_pool - the pool where the BE is, if different than
1745  *                      the pool containing the boot menu.  If this is
1746  *                      NULL it will be set to be_root_pool.
1747  * Returns:
1748  *              BE_SUCCESS - Success
1749  *              be_errno_t - Failure
1750  * Scope:
1751  *              Semi-private (library wide use only)
1752  */
1753 int
1754 be_update_menu(char *be_orig_name, char *be_new_name, char *be_root_pool,
1755     char *boot_pool)
1756 {
1757         zfs_handle_t *zhp = NULL;
1758         char menu_file[MAXPATHLEN];
1759         char be_root_ds[MAXPATHLEN];
1760         char be_new_root_ds[MAXPATHLEN];
1761         char line[BUFSIZ];
1762         char *pool_mntpnt = NULL;
1763         char *ptmp_mntpnt = NULL;
1764         char *orig_mntpnt = NULL;
1765         char *temp_menu = NULL;
1766         FILE *menu_fp = NULL;
1767         FILE *new_fp = NULL;
1768         struct stat sb;
1769         int temp_menu_len = 0;
1770         int tmp_fd;
1771         int ret = BE_SUCCESS;
1772         int err = 0;
1773         boolean_t pool_mounted = B_FALSE;
1774 
1775         errno = 0;
1776 
1777         if (boot_pool == NULL)
1778                 boot_pool = be_root_pool;
1779 
1780         if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
1781                 be_print_err(gettext("be_update_menu: failed to open "
1782                     "pool dataset for %s: %s\n"), be_root_pool,
1783                     libzfs_error_description(g_zfs));
1784                 return (zfs_err_to_be_err(g_zfs));
1785         }
1786 
1787         /*
1788          * Check to see if the pool's dataset is mounted. If it isn't we'll
1789          * attempt to mount it.
1790          */
1791         if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
1792             &pool_mounted)) != BE_SUCCESS) {
1793                 be_print_err(gettext("be_update_menu: pool dataset "
1794                     "(%s) could not be mounted\n"), be_root_pool);
1795                 ZFS_CLOSE(zhp);
1796                 return (ret);
1797         }
1798 
1799         /*
1800          * Get the mountpoint for the root pool dataset.
1801          */
1802         if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
1803                 be_print_err(gettext("be_update_menu: failed "
1804                     "to get mount point for the root pool. Can't set "
1805                     "the default BE in the grub menu.\n"));
1806                 ret = BE_ERR_NO_MENU;
1807                 goto cleanup;
1808         }
1809 
1810         /*
1811          * Check to see if this system supports grub
1812          */
1813         if (be_has_grub()) {
1814                 (void) snprintf(menu_file, sizeof (menu_file),
1815                     "%s%s", pool_mntpnt, BE_GRUB_MENU);
1816         } else {
1817                 (void) snprintf(menu_file, sizeof (menu_file),
1818                     "%s%s", pool_mntpnt, BE_SPARC_MENU);
1819         }
1820 
1821         be_make_root_ds(be_root_pool, be_orig_name, be_root_ds,
1822             sizeof (be_root_ds));
1823         be_make_root_ds(be_root_pool, be_new_name, be_new_root_ds,
1824             sizeof (be_new_root_ds));
1825 
1826         if ((ret = be_open_menu(be_root_pool, menu_file,
1827             &menu_fp, "r", B_TRUE)) != BE_SUCCESS) {
1828                 goto cleanup;
1829         } else if (menu_fp == NULL) {
1830                 ret = BE_ERR_NO_MENU;
1831                 goto cleanup;
1832         }
1833 
1834         free(pool_mntpnt);
1835         pool_mntpnt = NULL;
1836 
1837         /* Grab the stat of the original menu file */
1838         if (stat(menu_file, &sb) != 0) {
1839                 err = errno;
1840                 be_print_err(gettext("be_update_menu: "
1841                     "failed to stat file %s: %s\n"), menu_file, strerror(err));
1842                 (void) fclose(menu_fp);
1843                 ret = errno_to_be_err(err);
1844                 goto cleanup;
1845         }
1846 
1847         /* Create tmp file for modified menu.lst */
1848         temp_menu_len = strlen(menu_file) + 7;
1849         if ((temp_menu = (char *)malloc(temp_menu_len))
1850             == NULL) {
1851                 be_print_err(gettext("be_update_menu: "
1852                     "malloc failed\n"));
1853                 (void) fclose(menu_fp);
1854                 ret = BE_ERR_NOMEM;
1855                 goto cleanup;
1856         }
1857         (void) memset(temp_menu, 0, temp_menu_len);
1858         (void) strlcpy(temp_menu, menu_file, temp_menu_len);
1859         (void) strlcat(temp_menu, "XXXXXX", temp_menu_len);
1860         if ((tmp_fd = mkstemp(temp_menu)) == -1) {
1861                 err = errno;
1862                 be_print_err(gettext("be_update_menu: "
1863                     "mkstemp failed: %s\n"), strerror(err));
1864                 (void) fclose(menu_fp);
1865                 free(temp_menu);
1866                 ret = errno_to_be_err(err);
1867                 goto cleanup;
1868         }
1869         if ((new_fp = fdopen(tmp_fd, "w")) == NULL) {
1870                 err = errno;
1871                 be_print_err(gettext("be_update_menu: "
1872                     "fdopen failed: %s\n"), strerror(err));
1873                 (void) close(tmp_fd);
1874                 (void) fclose(menu_fp);
1875                 free(temp_menu);
1876                 ret = errno_to_be_err(err);
1877                 goto cleanup;
1878         }
1879 
1880         while (fgets(line, BUFSIZ, menu_fp)) {
1881                 char tline[BUFSIZ];
1882                 char new_line[BUFSIZ];
1883                 char *c = NULL;
1884 
1885                 (void) strlcpy(tline, line, sizeof (tline));
1886 
1887                 /* Tokenize line */
1888                 c = strtok(tline, BE_WHITE_SPACE);
1889 
1890                 if (c == NULL) {
1891                         /* Found empty line, write it out. */
1892                         (void) fputs(line, new_fp);
1893                 } else if (c[0] == '#') {
1894                         /* Found a comment line, write it out. */
1895                         (void) fputs(line, new_fp);
1896                 } else if (strcmp(c, "title") == 0) {
1897                         char *name = NULL;
1898                         char *desc = NULL;
1899 
1900                         /*
1901                          * Found a 'title' line, parse out BE name or
1902                          * the description.
1903                          */
1904                         name = strtok(NULL, BE_WHITE_SPACE);
1905 
1906                         if (name == NULL) {
1907                                 /*
1908                                  * Nothing after 'title', just push
1909                                  * this line through
1910                                  */
1911                                 (void) fputs(line, new_fp);
1912                         } else {
1913                                 /*
1914                                  * Grab the remainder of the title which
1915                                  * could be a multi worded description
1916                                  */
1917                                 desc = strtok(NULL, "\n");
1918 
1919                                 if (strcmp(name, be_orig_name) == 0) {
1920                                         /*
1921                                          * The first token of the title is
1922                                          * the old BE name, replace it with
1923                                          * the new one, and write it out
1924                                          * along with the remainder of
1925                                          * description if there is one.
1926                                          */
1927                                         if (desc) {
1928                                                 (void) snprintf(new_line,
1929                                                     sizeof (new_line),
1930                                                     "title %s %s\n",
1931                                                     be_new_name, desc);
1932                                         } else {
1933                                                 (void) snprintf(new_line,
1934                                                     sizeof (new_line),
1935                                                     "title %s\n", be_new_name);
1936                                         }
1937 
1938                                         (void) fputs(new_line, new_fp);
1939                                 } else {
1940                                         (void) fputs(line, new_fp);
1941                                 }
1942                         }
1943                 } else if (strcmp(c, "bootfs") == 0) {
1944                         /*
1945                          * Found a 'bootfs' line, parse out the BE root
1946                          * dataset value.
1947                          */
1948                         char *root_ds = strtok(NULL, BE_WHITE_SPACE);
1949 
1950                         if (root_ds == NULL) {
1951                                 /*
1952                                  * Nothing after 'bootfs', just push
1953                                  * this line through
1954                                  */
1955                                 (void) fputs(line, new_fp);
1956                         } else {
1957                                 /*
1958                                  * If this bootfs is the one we're renaming,
1959                                  * write out the new root dataset value
1960                                  */
1961                                 if (strcmp(root_ds, be_root_ds) == 0) {
1962                                         (void) snprintf(new_line,
1963                                             sizeof (new_line), "bootfs %s\n",
1964                                             be_new_root_ds);
1965 
1966                                         (void) fputs(new_line, new_fp);
1967                                 } else {
1968                                         (void) fputs(line, new_fp);
1969                                 }
1970                         }
1971                 } else {
1972                         /*
1973                          * Found some other line we don't care
1974                          * about, write it out.
1975                          */
1976                         (void) fputs(line, new_fp);
1977                 }
1978         }
1979 
1980         (void) fclose(menu_fp);
1981         (void) fclose(new_fp);
1982         (void) close(tmp_fd);
1983 
1984         if (rename(temp_menu, menu_file) != 0) {
1985                 err = errno;
1986                 be_print_err(gettext("be_update_menu: "
1987                     "failed to rename file %s to %s: %s\n"),
1988                     temp_menu, menu_file, strerror(err));
1989                 ret = errno_to_be_err(err);
1990         }
1991         free(temp_menu);
1992 
1993         /* Set the perms and ownership of the updated file */
1994         if (chmod(menu_file, sb.st_mode) != 0) {
1995                 err = errno;
1996                 be_print_err(gettext("be_update_menu: "
1997                     "failed to chmod %s: %s\n"), menu_file, strerror(err));
1998                 ret = errno_to_be_err(err);
1999                 goto cleanup;
2000         }
2001         if (chown(menu_file, sb.st_uid, sb.st_gid) != 0) {
2002                 err = errno;
2003                 be_print_err(gettext("be_update_menu: "
2004                     "failed to chown %s: %s\n"), menu_file, strerror(err));
2005                 ret = errno_to_be_err(err);
2006         }
2007 
2008 cleanup:
2009         if (pool_mounted) {
2010                 int err = BE_SUCCESS;
2011                 err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
2012                 if (ret == BE_SUCCESS)
2013                         ret = err;
2014                 free(orig_mntpnt);
2015                 free(ptmp_mntpnt);
2016         }
2017         ZFS_CLOSE(zhp);
2018         return (ret);
2019 }
2020 
2021 /*
2022  * Function:    be_has_menu_entry
2023  * Description: Checks to see if the BEs root dataset has an entry in the grub
2024  *              menu.
2025  * Parameters:
2026  *              be_dataset - The root dataset of the BE
2027  *              be_root_pool - The pool which contains the boot menu
2028  *              entry - A pointer the the entry number of the BE if found.
2029  * Returns:
2030  *              B_TRUE - Success
2031  *              B_FALSE - Failure
2032  * Scope:
2033  *              Semi-private (library wide use only)
2034  */
2035 boolean_t
2036 be_has_menu_entry(char *be_dataset, char *be_root_pool, int *entry)
2037 {
2038         zfs_handle_t *zhp = NULL;
2039         char            menu_file[MAXPATHLEN];
2040         FILE            *menu_fp;
2041         char            line[BUFSIZ];
2042         char            *last;
2043         char            *rpool_mntpnt = NULL;
2044         char            *ptmp_mntpnt = NULL;
2045         char            *orig_mntpnt = NULL;
2046         int             ent_num = 0;
2047         boolean_t       ret = 0;
2048         boolean_t       pool_mounted = B_FALSE;
2049 
2050 
2051         /*
2052          * Check to see if this system supports grub
2053          */
2054         if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
2055                 be_print_err(gettext("be_has_menu_entry: failed to open "
2056                     "pool dataset for %s: %s\n"), be_root_pool,
2057                     libzfs_error_description(g_zfs));
2058                 return (B_FALSE);
2059         }
2060 
2061         /*
2062          * Check to see if the pool's dataset is mounted. If it isn't we'll
2063          * attempt to mount it.
2064          */
2065         if (be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
2066             &pool_mounted) != 0) {
2067                 be_print_err(gettext("be_has_menu_entry: pool dataset "
2068                     "(%s) could not be mounted\n"), be_root_pool);
2069                 ZFS_CLOSE(zhp);
2070                 return (B_FALSE);
2071         }
2072 
2073         /*
2074          * Get the mountpoint for the root pool dataset.
2075          */
2076         if (!zfs_is_mounted(zhp, &rpool_mntpnt)) {
2077                 be_print_err(gettext("be_has_menu_entry: pool "
2078                     "dataset (%s) is not mounted. Can't set "
2079                     "the default BE in the grub menu.\n"), be_root_pool);
2080                 ret = B_FALSE;
2081                 goto cleanup;
2082         }
2083 
2084         if (be_has_grub()) {
2085                 (void) snprintf(menu_file, MAXPATHLEN, "/%s%s",
2086                     rpool_mntpnt, BE_GRUB_MENU);
2087         } else {
2088                 (void) snprintf(menu_file, MAXPATHLEN, "/%s%s",
2089                     rpool_mntpnt, BE_SPARC_MENU);
2090         }
2091 
2092         if (be_open_menu(be_root_pool, menu_file, &menu_fp, "r",
2093             B_FALSE) != 0) {
2094                 ret = B_FALSE;
2095                 goto cleanup;
2096         } else if (menu_fp == NULL) {
2097                 ret = B_FALSE;
2098                 goto cleanup;
2099         }
2100 
2101         free(rpool_mntpnt);
2102         rpool_mntpnt = NULL;
2103 
2104         while (fgets(line, BUFSIZ, menu_fp)) {
2105                 char *tok = strtok_r(line, BE_WHITE_SPACE, &last);
2106 
2107                 if (tok != NULL && tok[0] != '#') {
2108                         if (strcmp(tok, "bootfs") == 0) {
2109                                 tok = strtok_r(last, BE_WHITE_SPACE, &last);
2110                                 if (tok != NULL && strcmp(tok,
2111                                     be_dataset) == 0) {
2112                                         (void) fclose(menu_fp);
2113                                         /*
2114                                          * The entry number needs to be
2115                                          * decremented here because the title
2116                                          * will always be the first line for
2117                                          * an entry. Because of this we'll
2118                                          * always be off by one entry when we
2119                                          * check for bootfs.
2120                                          */
2121                                         *entry = ent_num - 1;
2122                                         ret = B_TRUE;
2123                                         goto cleanup;
2124                                 }
2125                         } else if (strcmp(tok, "title") == 0)
2126                                 ent_num++;
2127                 }
2128         }
2129 
2130 cleanup:
2131         if (pool_mounted) {
2132                 (void) be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
2133                 free(orig_mntpnt);
2134                 free(ptmp_mntpnt);
2135         }
2136         ZFS_CLOSE(zhp);
2137         (void) fclose(menu_fp);
2138         return (ret);
2139 }
2140 
2141 /*
2142  * Function:    be_update_vfstab
2143  * Description: This function digs into a BE's vfstab and updates all
2144  *              entries with file systems listed in be_fs_list_data_t.
2145  *              The entry's root container dataset and be_name will be
2146  *              updated with the parameters passed in.
2147  * Parameters:
2148  *              be_name - name of BE to update
2149  *              old_rc_loc - dataset under which the root container dataset
2150  *                      of the old BE resides in.
2151  *              new_rc_loc - dataset under which the root container dataset
2152  *                      of the new BE resides in.
2153  *              fld - be_fs_list_data_t pointer providing the list of
2154  *                      file systems to look for in vfstab.
2155  *              mountpoint - directory of where BE is currently mounted.
2156  *                      If NULL, then BE is not currently mounted.
2157  * Returns:
2158  *              BE_SUCCESS - Success
2159  *              be_errno_t - Failure
2160  * Scope:
2161  *              Semi-private (library wide use only)
2162  */
2163 int
2164 be_update_vfstab(char *be_name, char *old_rc_loc, char *new_rc_loc,
2165     be_fs_list_data_t *fld, char *mountpoint)
2166 {
2167         char            *tmp_mountpoint = NULL;
2168         char            alt_vfstab[MAXPATHLEN];
2169         int             ret = BE_SUCCESS, err = BE_SUCCESS;
2170 
2171         if (fld == NULL || fld->fs_list == NULL || fld->fs_num == 0)
2172                 return (BE_SUCCESS);
2173 
2174         /* If BE not already mounted, mount the BE */
2175         if (mountpoint == NULL) {
2176                 if ((ret = _be_mount(be_name, &tmp_mountpoint,
2177                     BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) {
2178                         be_print_err(gettext("be_update_vfstab: "
2179                             "failed to mount BE (%s)\n"), be_name);
2180                         return (ret);
2181                 }
2182         } else {
2183                 tmp_mountpoint = mountpoint;
2184         }
2185 
2186         /* Get string for vfstab in the mounted BE. */
2187         (void) snprintf(alt_vfstab, sizeof (alt_vfstab), "%s/etc/vfstab",
2188             tmp_mountpoint);
2189 
2190         /* Update the vfstab */
2191         ret = _update_vfstab(alt_vfstab, be_name, old_rc_loc, new_rc_loc,
2192             fld);
2193 
2194         /* Unmount BE if we mounted it */
2195         if (mountpoint == NULL) {
2196                 if ((err = _be_unmount(be_name, 0)) == BE_SUCCESS) {
2197                         /* Remove temporary mountpoint */
2198                         (void) rmdir(tmp_mountpoint);
2199                 } else {
2200                         be_print_err(gettext("be_update_vfstab: "
2201                             "failed to unmount BE %s mounted at %s\n"),
2202                             be_name, tmp_mountpoint);
2203                         if (ret == BE_SUCCESS)
2204                                 ret = err;
2205                 }
2206 
2207                 free(tmp_mountpoint);
2208         }
2209 
2210         return (ret);
2211 }
2212 
2213 /*
2214  * Function:    be_update_zone_vfstab
2215  * Description: This function digs into a zone BE's vfstab and updates all
2216  *              entries with file systems listed in be_fs_list_data_t.
2217  *              The entry's root container dataset and be_name will be
2218  *              updated with the parameters passed in.
2219  * Parameters:
2220  *              zhp - zfs_handle_t pointer to zone root dataset.
2221  *              be_name - name of zone BE to update
2222  *              old_rc_loc - dataset under which the root container dataset
2223  *                      of the old zone BE resides in.
2224  *              new_rc_loc - dataset under which the root container dataset
2225  *                      of the new zone BE resides in.
2226  *              fld - be_fs_list_data_t pointer providing the list of
2227  *                      file systems to look for in vfstab.
2228  * Returns:
2229  *              BE_SUCCESS - Success
2230  *              be_errno_t - Failure
2231  * Scope:
2232  *              Semi-private (library wide use only)
2233  */
2234 int
2235 be_update_zone_vfstab(zfs_handle_t *zhp, char *be_name, char *old_rc_loc,
2236     char *new_rc_loc, be_fs_list_data_t *fld)
2237 {
2238         be_mount_data_t         md = { 0 };
2239         be_unmount_data_t       ud = { 0 };
2240         char                    alt_vfstab[MAXPATHLEN];
2241         boolean_t               mounted_here = B_FALSE;
2242         int                     ret = BE_SUCCESS;
2243 
2244         /*
2245          * If zone root not already mounted, mount it at a
2246          * temporary location.
2247          */
2248         if (!zfs_is_mounted(zhp, &md.altroot)) {
2249                 /* Generate temporary mountpoint to mount zone root */
2250                 if ((ret = be_make_tmp_mountpoint(&md.altroot)) != BE_SUCCESS) {
2251                         be_print_err(gettext("be_update_zone_vfstab: "
2252                             "failed to make temporary mountpoint to "
2253                             "mount zone root\n"));
2254                         return (ret);
2255                 }
2256 
2257                 if (be_mount_zone_root(zhp, &md) != BE_SUCCESS) {
2258                         be_print_err(gettext("be_update_zone_vfstab: "
2259                             "failed to mount zone root %s\n"),
2260                             zfs_get_name(zhp));
2261                         free(md.altroot);
2262                         return (BE_ERR_MOUNT_ZONEROOT);
2263                 }
2264                 mounted_here = B_TRUE;
2265         }
2266 
2267         /* Get string from vfstab in the mounted zone BE */
2268         (void) snprintf(alt_vfstab, sizeof (alt_vfstab), "%s/etc/vfstab",
2269             md.altroot);
2270 
2271         /* Update the vfstab */
2272         ret = _update_vfstab(alt_vfstab, be_name, old_rc_loc, new_rc_loc,
2273             fld);
2274 
2275         /* Unmount zone root if we mounted it */
2276         if (mounted_here) {
2277                 ud.force = B_TRUE;
2278 
2279                 if (be_unmount_zone_root(zhp, &ud) == BE_SUCCESS) {
2280                         /* Remove the temporary mountpoint */
2281                         (void) rmdir(md.altroot);
2282                 } else {
2283                         be_print_err(gettext("be_update_zone_vfstab: "
2284                             "failed to unmount zone root %s from %s\n"),
2285                             zfs_get_name(zhp), md.altroot);
2286                         if (ret == 0)
2287                                 ret = BE_ERR_UMOUNT_ZONEROOT;
2288                 }
2289         }
2290 
2291         free(md.altroot);
2292         return (ret);
2293 }
2294 
2295 /*
2296  * Function:    be_auto_snap_name
2297  * Description: Generate an auto snapshot name constructed based on the
2298  *              current date and time.  The auto snapshot name is of the form:
2299  *
2300  *                      <date>-<time>
2301  *
2302  *              where <date> is in ISO standard format, so the resultant name
2303  *              is of the form:
2304  *
2305  *                      %Y-%m-%d-%H:%M:%S
2306  *
2307  * Parameters:
2308  *              None
2309  * Returns:
2310  *              Success - pointer to auto generated snapshot name.  The name
2311  *                      is allocated in heap storage so the caller is
2312  *                      responsible for free'ing the name.
2313  *              Failure - NULL
2314  * Scope:
2315  *              Semi-private (library wide use only)
2316  */
2317 char *
2318 be_auto_snap_name(void)
2319 {
2320         time_t          utc_tm = NULL;
2321         struct tm       *gmt_tm = NULL;
2322         char            gmt_time_str[64];
2323         char            *auto_snap_name = NULL;
2324 
2325         if (time(&utc_tm) == -1) {
2326                 be_print_err(gettext("be_auto_snap_name: time() failed\n"));
2327                 return (NULL);
2328         }
2329 
2330         if ((gmt_tm = gmtime(&utc_tm)) == NULL) {
2331                 be_print_err(gettext("be_auto_snap_name: gmtime() failed\n"));
2332                 return (NULL);
2333         }
2334 
2335         (void) strftime(gmt_time_str, sizeof (gmt_time_str), "%F-%T", gmt_tm);
2336 
2337         if ((auto_snap_name = strdup(gmt_time_str)) == NULL) {
2338                 be_print_err(gettext("be_auto_snap_name: "
2339                     "memory allocation failed\n"));
2340                 return (NULL);
2341         }
2342 
2343         return (auto_snap_name);
2344 }
2345 
2346 /*
2347  * Function:    be_auto_be_name
2348  * Description: Generate an auto BE name constructed based on the BE name
2349  *              of the original BE being cloned.
2350  * Parameters:
2351  *              obe_name - name of the original BE being cloned.
2352  * Returns:
2353  *              Success - pointer to auto generated BE name.  The name
2354  *                      is allocated in heap storage so the caller is
2355  *                      responsible for free'ing the name.
2356  *              Failure - NULL
2357  * Scope:
2358  *              Semi-private (library wide use only)
2359  */
2360 char *
2361 be_auto_be_name(char *obe_name)
2362 {
2363         return (be_get_auto_name(obe_name, NULL, B_FALSE));
2364 }
2365 
2366 /*
2367  * Function:    be_auto_zone_be_name
2368  * Description: Generate an auto BE name for a zone constructed based on
2369  *              the BE name of the original zone BE being cloned.
2370  * Parameters:
2371  *              container_ds - container dataset for the zone.
2372  *              zbe_name - name of the original zone BE being cloned.
2373  * Returns:
2374  *              Success - pointer to auto generated BE name.  The name
2375  *                      is allocated in heap storage so the caller is
2376  *                      responsible for free'ing the name.
2377  *              Failure - NULL
2378  * Scope:
2379  *              Semi-private (library wide use only)
2380  */
2381 char *
2382 be_auto_zone_be_name(char *container_ds, char *zbe_name)
2383 {
2384         return (be_get_auto_name(zbe_name, container_ds, B_TRUE));
2385 }
2386 
2387 /*
2388  * Function:    be_valid_be_name
2389  * Description: Validates a BE name.
2390  * Parameters:
2391  *              be_name - name of BE to validate
2392  * Returns:
2393  *              B_TRUE - be_name is valid
2394  *              B_FALSE - be_name is invalid
2395  * Scope:
2396  *              Semi-private (library wide use only)
2397  */
2398 
2399 boolean_t
2400 be_valid_be_name(const char *be_name)
2401 {
2402         const char      *c = NULL;
2403         struct be_defaults be_defaults;
2404 
2405         if (be_name == NULL)
2406                 return (B_FALSE);
2407 
2408         be_get_defaults(&be_defaults);
2409 
2410         /*
2411          * A BE name must not be a multi-level dataset name.  We also check
2412          * that it does not contain the ' ' and '%' characters.  The ' ' is
2413          * a valid character for datasets, however we don't allow that in a
2414          * BE name.  The '%' is invalid, but zfs_name_valid() allows it for
2415          * internal reasons, so we explicitly check for it here.
2416          */
2417         c = be_name;
2418         while (*c != '\0' && *c != '/' && *c != ' ' && *c != '%')
2419                 c++;
2420 
2421         if (*c != '\0')
2422                 return (B_FALSE);
2423 
2424         /*
2425          * The BE name must comply with a zfs dataset filesystem. We also
2426          * verify its length to be < BE_NAME_MAX_LEN.
2427          */
2428         if (!zfs_name_valid(be_name, ZFS_TYPE_FILESYSTEM) ||
2429             strlen(be_name) > BE_NAME_MAX_LEN)
2430                 return (B_FALSE);
2431 
2432         if (be_defaults.be_deflt_bename_starts_with[0] != '\0' &&
2433             strstr(be_name, be_defaults.be_deflt_bename_starts_with) == NULL) {
2434                 return (B_FALSE);
2435         }
2436 
2437         return (B_TRUE);
2438 }
2439 
2440 /*
2441  * Function:    be_valid_auto_snap_name
2442  * Description: This function checks that a snapshot name is a valid auto
2443  *              generated snapshot name.  A valid auto generated snapshot
2444  *              name is of the form:
2445  *
2446  *                      %Y-%m-%d-%H:%M:%S
2447  *
2448  *              An older form of the auto generated snapshot name also
2449  *              included the snapshot's BE cleanup policy and a reserved
2450  *              field.  Those names will also be verified by this function.
2451  *
2452  *              Examples of valid auto snapshot names are:
2453  *
2454  *                      2008-03-31-18:41:30
2455  *                      2008-03-31-22:17:24
2456  *                      <policy>:-:2008:04-05-09:12:55
2457  *                      <policy>:-:2008:04-06-15:34:12
2458  *
2459  * Parameters:
2460  *              name - name of the snapshot to be validated.
2461  * Returns:
2462  *              B_TRUE - the name is a valid auto snapshot name.
2463  *              B_FALSE - the name is not a valid auto snapshot name.
2464  * Scope:
2465  *              Semi-private (library wide use only)
2466  */
2467 boolean_t
2468 be_valid_auto_snap_name(char *name)
2469 {
2470         struct tm gmt_tm;
2471 
2472         char *policy = NULL;
2473         char *reserved = NULL;
2474         char *date = NULL;
2475         char *c = NULL;
2476 
2477         /* Validate the snapshot name by converting it into utc time */
2478         if (strptime(name, "%Y-%m-%d-%T", &gmt_tm) != NULL &&
2479             (mktime(&gmt_tm) != -1)) {
2480                 return (B_TRUE);
2481         }
2482 
2483         /*
2484          * Validate the snapshot name against the older form of an
2485          * auto generated snapshot name.
2486          */
2487         policy = strdup(name);
2488 
2489         /*
2490          * Get the first field from the snapshot name,
2491          * which is the BE policy
2492          */
2493         c = strchr(policy, ':');
2494         if (c == NULL) {
2495                 free(policy);
2496                 return (B_FALSE);
2497         }
2498         c[0] = '\0';
2499 
2500         /* Validate the policy name */
2501         if (!valid_be_policy(policy)) {
2502                 free(policy);
2503                 return (B_FALSE);
2504         }
2505 
2506         /* Get the next field, which is the reserved field. */
2507         if (c[1] == NULL || c[1] == '\0') {
2508                 free(policy);
2509                 return (B_FALSE);
2510         }
2511         reserved = c+1;
2512         c = strchr(reserved, ':');
2513         if (c == NULL) {
2514                 free(policy);
2515                 return (B_FALSE);
2516         }
2517         c[0] = '\0';
2518 
2519         /* Validate the reserved field */
2520         if (strcmp(reserved, "-") != 0) {
2521                 free(policy);
2522                 return (B_FALSE);
2523         }
2524 
2525         /* The remaining string should be the date field */
2526         if (c[1] == NULL || c[1] == '\0') {
2527                 free(policy);
2528                 return (B_FALSE);
2529         }
2530         date = c+1;
2531 
2532         /* Validate the date string by converting it into utc time */
2533         if (strptime(date, "%Y-%m-%d-%T", &gmt_tm) == NULL ||
2534             (mktime(&gmt_tm) == -1)) {
2535                 be_print_err(gettext("be_valid_auto_snap_name: "
2536                     "invalid auto snapshot name\n"));
2537                 free(policy);
2538                 return (B_FALSE);
2539         }
2540 
2541         free(policy);
2542         return (B_TRUE);
2543 }
2544 
2545 /*
2546  * Function:    be_default_policy
2547  * Description: Temporary hardcoded policy support.  This function returns
2548  *              the default policy type to be used to create a BE or a BE
2549  *              snapshot.
2550  * Parameters:
2551  *              None
2552  * Returns:
2553  *              Name of default BE policy.
2554  * Scope:
2555  *              Semi-private (library wide use only)
2556  */
2557 char *
2558 be_default_policy(void)
2559 {
2560         return (BE_PLCY_STATIC);
2561 }
2562 
2563 /*
2564  * Function:    valid_be_policy
2565  * Description: Temporary hardcoded policy support.  This function valids
2566  *              whether a policy is a valid known policy or not.
2567  * Paramters:
2568  *              policy - name of policy to validate.
2569  * Returns:
2570  *              B_TRUE - policy is a valid.
2571  *              B_FALSE - policy is invalid.
2572  * Scope:
2573  *              Semi-private (library wide use only)
2574  */
2575 boolean_t
2576 valid_be_policy(char *policy)
2577 {
2578         if (policy == NULL)
2579                 return (B_FALSE);
2580 
2581         if (strcmp(policy, BE_PLCY_STATIC) == 0 ||
2582             strcmp(policy, BE_PLCY_VOLATILE) == 0) {
2583                 return (B_TRUE);
2584         }
2585 
2586         return (B_FALSE);
2587 }
2588 
2589 /*
2590  * Function:    be_print_err
2591  * Description: This function prints out error messages if do_print is
2592  *              set to B_TRUE or if the BE_PRINT_ERR environment variable
2593  *              is set to true.
2594  * Paramters:
2595  *              prnt_str - the string we wish to print and any arguments
2596  *              for the format of that string.
2597  * Returns:
2598  *              void
2599  * Scope:
2600  *              Semi-private (library wide use only)
2601  */
2602 void
2603 be_print_err(char *prnt_str, ...)
2604 {
2605         va_list ap;
2606         char buf[BUFSIZ];
2607         char *env_buf;
2608         static boolean_t env_checked = B_FALSE;
2609 
2610         if (!env_checked) {
2611                 if ((env_buf = getenv("BE_PRINT_ERR")) != NULL) {
2612                         if (strcasecmp(env_buf, "true") == 0) {
2613                                 do_print = B_TRUE;
2614                         }
2615                 }
2616                 env_checked = B_TRUE;
2617         }
2618 
2619         if (do_print) {
2620                 va_start(ap, prnt_str);
2621                 /* LINTED variable format specifier */
2622                 (void) vsnprintf(buf, BUFSIZ, prnt_str, ap);
2623                 (void) fputs(buf, stderr);
2624                 va_end(ap);
2625         }
2626 }
2627 
2628 /*
2629  * Function:    be_find_current_be
2630  * Description: Find the currently "active" BE. Fill in the
2631  *              passed in be_transaction_data_t reference with the
2632  *              active BE's data.
2633  * Paramters:
2634  *              none
2635  * Returns:
2636  *              BE_SUCCESS - Success
2637  *              be_errnot_t - Failure
2638  * Scope:
2639  *              Semi-private (library wide use only)
2640  * Notes:
2641  *              The caller is responsible for initializing the libzfs handle
2642  *              and freeing the memory used by the active be_name.
2643  */
2644 int
2645 be_find_current_be(be_transaction_data_t *bt)
2646 {
2647         int     zret;
2648 
2649         if ((zret = zpool_iter(g_zfs, be_zpool_find_current_be_callback,
2650             bt)) == 0) {
2651                 be_print_err(gettext("be_find_current_be: failed to "
2652                     "find current BE name\n"));
2653                 return (BE_ERR_BE_NOENT);
2654         } else if (zret < 0) {
2655                 be_print_err(gettext("be_find_current_be: "
2656                     "zpool_iter failed: %s\n"),
2657                     libzfs_error_description(g_zfs));
2658                 return (zfs_err_to_be_err(g_zfs));
2659         }
2660 
2661         return (BE_SUCCESS);
2662 }
2663 
2664 /*
2665  * Function:    be_zpool_find_current_be_callback
2666  * Description: Callback function used to iterate through all existing pools
2667  *              to find the BE that is the currently booted BE.
2668  * Parameters:
2669  *              zlp - zpool_handle_t pointer to the current pool being
2670  *                      looked at.
2671  *              data - be_transaction_data_t pointer.
2672  *                      Upon successfully finding the current BE, the
2673  *                      obe_zpool member of this parameter is set to the
2674  *                      pool it is found in.
2675  * Return:
2676  *              1 - Found current BE in this pool.
2677  *              0 - Did not find current BE in this pool.
2678  * Scope:
2679  *              Semi-private (library wide use only)
2680  */
2681 int
2682 be_zpool_find_current_be_callback(zpool_handle_t *zlp, void *data)
2683 {
2684         be_transaction_data_t   *bt = data;
2685         zfs_handle_t            *zhp = NULL;
2686         const char              *zpool =  zpool_get_name(zlp);
2687         char                    be_container_ds[MAXPATHLEN];
2688         char                    *zpath = NULL;
2689 
2690         /*
2691          * Generate string for BE container dataset
2692          */
2693         if (getzoneid() != GLOBAL_ZONEID) {
2694                 if ((zpath = be_get_ds_from_dir("/")) != NULL) {
2695                         (void) strlcpy(be_container_ds, dirname(zpath),
2696                             sizeof (be_container_ds));
2697                 } else {
2698                         be_print_err(gettext(
2699                             "be_zpool_find_current_be_callback: "
2700                             "zone root dataset is not mounted\n"));
2701                         return (0);
2702                 }
2703         } else {
2704                 be_make_container_ds(zpool, be_container_ds,
2705                     sizeof (be_container_ds));
2706         }
2707 
2708         /*
2709          * Check if a BE container dataset exists in this pool.
2710          */
2711         if (!zfs_dataset_exists(g_zfs, be_container_ds, ZFS_TYPE_FILESYSTEM)) {
2712                 zpool_close(zlp);
2713                 return (0);
2714         }
2715 
2716         /*
2717          * Get handle to this zpool's BE container dataset.
2718          */
2719         if ((zhp = zfs_open(g_zfs, be_container_ds, ZFS_TYPE_FILESYSTEM)) ==
2720             NULL) {
2721                 be_print_err(gettext("be_zpool_find_current_be_callback: "
2722                     "failed to open BE container dataset (%s)\n"),
2723                     be_container_ds);
2724                 zpool_close(zlp);
2725                 return (0);
2726         }
2727 
2728         /*
2729          * Iterate through all potential BEs in this zpool
2730          */
2731         if (zfs_iter_filesystems(zhp, be_zfs_find_current_be_callback, bt)) {
2732                 /*
2733                  * Found current BE dataset; set obe_zpool
2734                  */
2735                 if ((bt->obe_zpool = strdup(zpool)) == NULL) {
2736                         be_print_err(gettext(
2737                             "be_zpool_find_current_be_callback: "
2738                             "memory allocation failed\n"));
2739                         ZFS_CLOSE(zhp);
2740                         zpool_close(zlp);
2741                         return (0);
2742                 }
2743 
2744                 ZFS_CLOSE(zhp);
2745                 zpool_close(zlp);
2746                 return (1);
2747         }
2748 
2749         ZFS_CLOSE(zhp);
2750         zpool_close(zlp);
2751 
2752         return (0);
2753 }
2754 
2755 /*
2756  * Function:    be_zfs_find_current_be_callback
2757  * Description: Callback function used to iterate through all BEs in a
2758  *              pool to find the BE that is the currently booted BE.
2759  * Parameters:
2760  *              zhp - zfs_handle_t pointer to current filesystem being checked.
2761  *              data - be_transaction-data_t pointer
2762  *                      Upon successfully finding the current BE, the
2763  *                      obe_name and obe_root_ds members of this parameter
2764  *                      are set to the BE name and BE's root dataset
2765  *                      respectively.
2766  * Return:
2767  *              1 - Found current BE.
2768  *              0 - Did not find current BE.
2769  * Scope:
2770  *              Semi-private (library wide use only)
2771  */
2772 int
2773 be_zfs_find_current_be_callback(zfs_handle_t *zhp, void *data)
2774 {
2775         be_transaction_data_t   *bt = data;
2776         char                    *mp = NULL;
2777 
2778         /*
2779          * Check if dataset is mounted, and if so where.
2780          */
2781         if (zfs_is_mounted(zhp, &mp)) {
2782                 /*
2783                  * If mounted at root, set obe_root_ds and obe_name
2784                  */
2785                 if (mp != NULL && strcmp(mp, "/") == 0) {
2786                         free(mp);
2787 
2788                         if ((bt->obe_root_ds = strdup(zfs_get_name(zhp)))
2789                             == NULL) {
2790                                 be_print_err(gettext(
2791                                     "be_zfs_find_current_be_callback: "
2792                                     "memory allocation failed\n"));
2793                                 ZFS_CLOSE(zhp);
2794                                 return (0);
2795                         }
2796 
2797                         if ((bt->obe_name = strdup(basename(bt->obe_root_ds)))
2798                             == NULL) {
2799                                 be_print_err(gettext(
2800                                     "be_zfs_find_current_be_callback: "
2801                                     "memory allocation failed\n"));
2802                                 ZFS_CLOSE(zhp);
2803                                 return (0);
2804                         }
2805 
2806                         ZFS_CLOSE(zhp);
2807                         return (1);
2808                 }
2809 
2810                 free(mp);
2811         }
2812         ZFS_CLOSE(zhp);
2813 
2814         return (0);
2815 }
2816 
2817 /*
2818  * Function:    be_check_be_roots_callback
2819  * Description: This function checks whether or not the dataset name passed
2820  *              is hierachically located under the BE root container dataset
2821  *              for this pool.
2822  * Parameters:
2823  *              zlp - zpool_handle_t pointer to current pool being processed.
2824  *              data - name of dataset to check
2825  * Returns:
2826  *              0 - dataset is not in this pool's BE root container dataset
2827  *              1 - dataset is in this pool's BE root container dataset
2828  * Scope:
2829  *              Semi-private (library wide use only)
2830  */
2831 int
2832 be_check_be_roots_callback(zpool_handle_t *zlp, void *data)
2833 {
2834         const char      *zpool = zpool_get_name(zlp);
2835         char            *ds = data;
2836         char            be_container_ds[MAXPATHLEN];
2837 
2838         /* Generate string for this pool's BE root container dataset */
2839         be_make_container_ds(zpool, be_container_ds, sizeof (be_container_ds));
2840 
2841         /*
2842          * If dataset lives under the BE root container dataset
2843          * of this pool, return failure.
2844          */
2845         if (strncmp(be_container_ds, ds, strlen(be_container_ds)) == 0 &&
2846             ds[strlen(be_container_ds)] == '/') {
2847                 zpool_close(zlp);
2848                 return (1);
2849         }
2850 
2851         zpool_close(zlp);
2852         return (0);
2853 }
2854 
2855 /*
2856  * Function:    zfs_err_to_be_err
2857  * Description: This function takes the error stored in the libzfs handle
2858  *              and maps it to an be_errno_t. If there are no matching
2859  *              be_errno_t's then BE_ERR_ZFS is returned.
2860  * Paramters:
2861  *              zfsh - The libzfs handle containing the error we're looking up.
2862  * Returns:
2863  *              be_errno_t
2864  * Scope:
2865  *              Semi-private (library wide use only)
2866  */
2867 int
2868 zfs_err_to_be_err(libzfs_handle_t *zfsh)
2869 {
2870         int err = libzfs_errno(zfsh);
2871 
2872         switch (err) {
2873         case 0:
2874                 return (BE_SUCCESS);
2875         case EZFS_PERM:
2876                 return (BE_ERR_PERM);
2877         case EZFS_INTR:
2878                 return (BE_ERR_INTR);
2879         case EZFS_NOENT:
2880                 return (BE_ERR_NOENT);
2881         case EZFS_NOSPC:
2882                 return (BE_ERR_NOSPC);
2883         case EZFS_MOUNTFAILED:
2884                 return (BE_ERR_MOUNT);
2885         case EZFS_UMOUNTFAILED:
2886                 return (BE_ERR_UMOUNT);
2887         case EZFS_EXISTS:
2888                 return (BE_ERR_BE_EXISTS);
2889         case EZFS_BUSY:
2890                 return (BE_ERR_DEV_BUSY);
2891         case EZFS_POOLREADONLY:
2892                 return (BE_ERR_ROFS);
2893         case EZFS_NAMETOOLONG:
2894                 return (BE_ERR_NAMETOOLONG);
2895         case EZFS_NODEVICE:
2896                 return (BE_ERR_NODEV);
2897         case EZFS_POOL_INVALARG:
2898                 return (BE_ERR_INVAL);
2899         case EZFS_PROPTYPE:
2900                 return (BE_ERR_INVALPROP);
2901         case EZFS_BADTYPE:
2902                 return (BE_ERR_DSTYPE);
2903         case EZFS_PROPNONINHERIT:
2904                 return (BE_ERR_NONINHERIT);
2905         case EZFS_PROPREADONLY:
2906                 return (BE_ERR_READONLYPROP);
2907         case EZFS_RESILVERING:
2908         case EZFS_POOLUNAVAIL:
2909                 return (BE_ERR_UNAVAIL);
2910         case EZFS_DSREADONLY:
2911                 return (BE_ERR_READONLYDS);
2912         default:
2913                 return (BE_ERR_ZFS);
2914         }
2915 }
2916 
2917 /*
2918  * Function:    errno_to_be_err
2919  * Description: This function takes an errno and maps it to an be_errno_t.
2920  *              If there are no matching be_errno_t's then BE_ERR_UNKNOWN is
2921  *              returned.
2922  * Paramters:
2923  *              err - The errno we're compairing against.
2924  * Returns:
2925  *              be_errno_t
2926  * Scope:
2927  *              Semi-private (library wide use only)
2928  */
2929 int
2930 errno_to_be_err(int err)
2931 {
2932         switch (err) {
2933         case EPERM:
2934                 return (BE_ERR_PERM);
2935         case EACCES:
2936                 return (BE_ERR_ACCESS);
2937         case ECANCELED:
2938                 return (BE_ERR_CANCELED);
2939         case EINTR:
2940                 return (BE_ERR_INTR);
2941         case ENOENT:
2942                 return (BE_ERR_NOENT);
2943         case ENOSPC:
2944         case EDQUOT:
2945                 return (BE_ERR_NOSPC);
2946         case EEXIST:
2947                 return (BE_ERR_BE_EXISTS);
2948         case EBUSY:
2949                 return (BE_ERR_BUSY);
2950         case EROFS:
2951                 return (BE_ERR_ROFS);
2952         case ENAMETOOLONG:
2953                 return (BE_ERR_NAMETOOLONG);
2954         case ENXIO:
2955                 return (BE_ERR_NXIO);
2956         case EINVAL:
2957                 return (BE_ERR_INVAL);
2958         case EFAULT:
2959                 return (BE_ERR_FAULT);
2960         default:
2961                 return (BE_ERR_UNKNOWN);
2962         }
2963 }
2964 
2965 /*
2966  * Function:    be_err_to_str
2967  * Description: This function takes a be_errno_t and maps it to a message.
2968  *              If there are no matching be_errno_t's then NULL is returned.
2969  * Paramters:
2970  *              be_errno_t - The be_errno_t we're mapping.
2971  * Returns:
2972  *              string or NULL if the error code is not known.
2973  * Scope:
2974  *              Semi-private (library wide use only)
2975  */
2976 char *
2977 be_err_to_str(int err)
2978 {
2979         switch (err) {
2980         case BE_ERR_ACCESS:
2981                 return (gettext("Permission denied."));
2982         case BE_ERR_ACTIVATE_CURR:
2983                 return (gettext("Activation of current BE failed."));
2984         case BE_ERR_AUTONAME:
2985                 return (gettext("Auto naming failed."));
2986         case BE_ERR_BE_NOENT:
2987                 return (gettext("No such BE."));
2988         case BE_ERR_BUSY:
2989                 return (gettext("Mount busy."));
2990         case BE_ERR_DEV_BUSY:
2991                 return (gettext("Device busy."));
2992         case BE_ERR_CANCELED:
2993                 return (gettext("Operation canceled."));
2994         case BE_ERR_CLONE:
2995                 return (gettext("BE clone failed."));
2996         case BE_ERR_COPY:
2997                 return (gettext("BE copy failed."));
2998         case BE_ERR_CREATDS:
2999                 return (gettext("Dataset creation failed."));
3000         case BE_ERR_CURR_BE_NOT_FOUND:
3001                 return (gettext("Can't find current BE."));
3002         case BE_ERR_DESTROY:
3003                 return (gettext("Failed to destroy BE or snapshot."));
3004         case BE_ERR_DESTROY_CURR_BE:
3005                 return (gettext("Cannot destroy current BE."));
3006         case BE_ERR_DEMOTE:
3007                 return (gettext("BE demotion failed."));
3008         case BE_ERR_DSTYPE:
3009                 return (gettext("Invalid dataset type."));
3010         case BE_ERR_BE_EXISTS:
3011                 return (gettext("BE exists."));
3012         case BE_ERR_INIT:
3013                 return (gettext("be_zfs_init failed."));
3014         case BE_ERR_INTR:
3015                 return (gettext("Interupted system call."));
3016         case BE_ERR_INVAL:
3017                 return (gettext("Invalid argument."));
3018         case BE_ERR_INVALPROP:
3019                 return (gettext("Invalid property for dataset."));
3020         case BE_ERR_INVALMOUNTPOINT:
3021                 return (gettext("Unexpected mountpoint."));
3022         case BE_ERR_MOUNT:
3023                 return (gettext("Mount failed."));
3024         case BE_ERR_MOUNTED:
3025                 return (gettext("Already mounted."));
3026         case BE_ERR_NAMETOOLONG:
3027                 return (gettext("name > BUFSIZ."));
3028         case BE_ERR_NOENT:
3029                 return (gettext("Doesn't exist."));
3030         case BE_ERR_POOL_NOENT:
3031                 return (gettext("No such pool."));
3032         case BE_ERR_NODEV:
3033                 return (gettext("No such device."));
3034         case BE_ERR_NOTMOUNTED:
3035                 return (gettext("File system not mounted."));
3036         case BE_ERR_NOMEM:
3037                 return (gettext("Not enough memory."));
3038         case BE_ERR_NONINHERIT:
3039                 return (gettext(
3040                     "Property is not inheritable for the BE dataset."));
3041         case BE_ERR_NXIO:
3042                 return (gettext("No such device or address."));
3043         case BE_ERR_NOSPC:
3044                 return (gettext("No space on device."));
3045         case BE_ERR_NOTSUP:
3046                 return (gettext("Operation not supported."));
3047         case BE_ERR_OPEN:
3048                 return (gettext("Open failed."));
3049         case BE_ERR_PERM:
3050                 return (gettext("Not owner."));
3051         case BE_ERR_UNAVAIL:
3052                 return (gettext("The BE is currently unavailable."));
3053         case BE_ERR_PROMOTE:
3054                 return (gettext("BE promotion failed."));
3055         case BE_ERR_ROFS:
3056                 return (gettext("Read only file system."));
3057         case BE_ERR_READONLYDS:
3058                 return (gettext("Read only dataset."));
3059         case BE_ERR_READONLYPROP:
3060                 return (gettext("Read only property."));
3061         case BE_ERR_RENAME_ACTIVE:
3062                 return (gettext("Renaming the active BE is not supported."));
3063         case BE_ERR_SS_EXISTS:
3064                 return (gettext("Snapshot exists."));
3065         case BE_ERR_SS_NOENT:
3066                 return (gettext("No such snapshot."));
3067         case BE_ERR_UMOUNT:
3068                 return (gettext("Unmount failed."));
3069         case BE_ERR_UMOUNT_CURR_BE:
3070                 return (gettext("Can't unmount the current BE."));
3071         case BE_ERR_UMOUNT_SHARED:
3072                 return (gettext("Unmount of a shared File System failed."));
3073         case BE_ERR_FAULT:
3074                 return (gettext("Bad address."));
3075         case BE_ERR_UNKNOWN:
3076                 return (gettext("Unknown error."));
3077         case BE_ERR_ZFS:
3078                 return (gettext("ZFS returned an error."));
3079         case BE_ERR_GEN_UUID:
3080                 return (gettext("Failed to generate uuid."));
3081         case BE_ERR_PARSE_UUID:
3082                 return (gettext("Failed to parse uuid."));
3083         case BE_ERR_NO_UUID:
3084                 return (gettext("No uuid"));
3085         case BE_ERR_ZONE_NO_PARENTBE:
3086                 return (gettext("No parent uuid"));
3087         case BE_ERR_ZONE_MULTIPLE_ACTIVE:
3088                 return (gettext("Multiple active zone roots"));
3089         case BE_ERR_ZONE_NO_ACTIVE_ROOT:
3090                 return (gettext("No active zone root"));
3091         case BE_ERR_ZONE_ROOT_NOT_LEGACY:
3092                 return (gettext("Zone root not legacy"));
3093         case BE_ERR_MOUNT_ZONEROOT:
3094                 return (gettext("Failed to mount a zone root."));
3095         case BE_ERR_UMOUNT_ZONEROOT:
3096                 return (gettext("Failed to unmount a zone root."));
3097         case BE_ERR_NO_MOUNTED_ZONE:
3098                 return (gettext("Zone is not mounted"));
3099         case BE_ERR_ZONES_UNMOUNT:
3100                 return (gettext("Unable to unmount a zone BE."));
3101         case BE_ERR_NO_MENU:
3102                 return (gettext("Missing boot menu file."));
3103         case BE_ERR_BAD_MENU_PATH:
3104                 return (gettext("Invalid path for menu.lst file"));
3105         case BE_ERR_ZONE_SS_EXISTS:
3106                 return (gettext("Zone snapshot exists."));
3107         case BE_ERR_BOOTFILE_INST:
3108                 return (gettext("Error installing boot files."));
3109         case BE_ERR_EXTCMD:
3110                 return (gettext("Error running an external command."));
3111         default:
3112                 return (NULL);
3113         }
3114 }
3115 
3116 /*
3117  * Function:    be_has_grub
3118  * Description: Boolean function indicating whether the current system
3119  *              uses grub.
3120  * Return:      B_FALSE - the system does not have grub
3121  *              B_TRUE - the system does have grub.
3122  * Scope:
3123  *              Semi-private (library wide use only)
3124  */
3125 boolean_t
3126 be_has_grub(void)
3127 {
3128         static struct be_defaults be_defaults;
3129         static boolean_t be_deflts_set = B_FALSE;
3130 
3131         /* Cache the defaults, because be_has_grub is used often. */
3132         if (be_deflts_set == B_FALSE) {
3133                 be_get_defaults(&be_defaults);
3134                 be_deflts_set = B_TRUE;
3135         }
3136 
3137         return (be_defaults.be_deflt_grub);
3138 }
3139 
3140 /*
3141  * Function:    be_is_isa
3142  * Description: Boolean function indicating whether the instruction set
3143  *              architecture of the executing system matches the name provided.
3144  *              The string must match a system defined architecture (e.g.
3145  *              "i386", "sparc") and is case sensitive.
3146  * Parameters:  name - string representing the name of instruction set
3147  *                      architecture being tested
3148  * Returns:     B_FALSE - the system instruction set architecture is different
3149  *                      from the one specified
3150  *              B_TRUE - the system instruction set architecture is the same
3151  *                      as the one specified
3152  * Scope:
3153  *              Semi-private (library wide use only)
3154  */
3155 boolean_t
3156 be_is_isa(char *name)
3157 {
3158         return ((strcmp((char *)be_get_default_isa(), name) == 0));
3159 }
3160 
3161 /*
3162  * Function: be_get_default_isa
3163  * Description:
3164  *      Returns the default instruction set architecture of the
3165  *      machine it is executed on. (eg. sparc, i386, ...)
3166  *      NOTE:   SYS_INST environment variable may override default
3167  *              return value
3168  * Parameters:
3169  *              none
3170  * Returns:
3171  *              NULL - the architecture returned by sysinfo() was too
3172  *                      long for local variables
3173  *              char * - pointer to a string containing the default
3174  *                      implementation
3175  * Scope:
3176  *              Semi-private (library wide use only)
3177  */
3178 char *
3179 be_get_default_isa(void)
3180 {
3181         int     i;
3182         char    *envp;
3183         static char     default_inst[ARCH_LENGTH] = "";
3184 
3185         if (default_inst[0] == '\0') {
3186                 if ((envp = getenv("SYS_INST")) != NULL) {
3187                         if ((int)strlen(envp) >= ARCH_LENGTH)
3188                                 return (NULL);
3189                         else
3190                                 (void) strcpy(default_inst, envp);
3191                 } else  {
3192                         i = sysinfo(SI_ARCHITECTURE, default_inst, ARCH_LENGTH);
3193                         if (i < 0 || i > ARCH_LENGTH)
3194                                 return (NULL);
3195                 }
3196         }
3197         return (default_inst);
3198 }
3199 
3200 /*
3201  * Function: be_get_platform
3202  * Description:
3203  *      Returns the platfom name
3204  * Parameters:
3205  *              none
3206  * Returns:
3207  *              NULL - the platform name returned by sysinfo() was too
3208  *                      long for local variables
3209  *              char * - pointer to a string containing the platform name
3210  * Scope:
3211  *              Semi-private (library wide use only)
3212  */
3213 char *
3214 be_get_platform(void)
3215 {
3216         int     i;
3217         static char     default_inst[ARCH_LENGTH] = "";
3218 
3219         if (default_inst[0] == '\0') {
3220                 i = sysinfo(SI_PLATFORM, default_inst, ARCH_LENGTH);
3221                 if (i < 0 || i > ARCH_LENGTH)
3222                         return (NULL);
3223         }
3224         return (default_inst);
3225 }
3226 
3227 /*
3228  * Function: be_run_cmd
3229  * Description:
3230  *      Runs a command in a separate subprocess.  Splits out stdout from stderr
3231  *      and sends each to its own buffer.  Buffers must be pre-allocated and
3232  *      passed in as arguments.  Buffer sizes are also passed in as arguments.
3233  *
3234  *      Notes / caveats:
3235  *      - Command being run is assumed to not have any stdout or stderr
3236  *              redirection.
3237  *      - Commands which emit total stderr output of greater than PIPE_BUF
3238  *              bytes can hang.  For such commands, a different implementation
3239  *              which uses poll(2) must be used.
3240  *      - stdout_buf can be NULL.  In this case, stdout_bufsize is ignored, and
3241  *              the stream which would have gone to it is sent to the bit
3242  *              bucket.
3243  *      - stderr_buf cannot be NULL.
3244  *      - Only subprocess errors are appended to the stderr_buf.  Errors
3245  *              running the command are reported through be_print_err().
3246  *      - Data which would overflow its respective buffer is sent to the bit
3247  *              bucket.
3248  *
3249  * Parameters:
3250  *              command: command to run.  Assumed not to have embedded stdout
3251  *                      or stderr redirection.  May have stdin redirection,
3252  *                      however.
3253  *              stderr_buf: buffer returning subprocess stderr data.  Errors
3254  *                      reported by this function are reported through
3255  *                      be_print_err().
3256  *              stderr_bufsize: size of stderr_buf
3257  *              stdout_buf: buffer returning subprocess stdout data.
3258  *              stdout_bufsize: size of stdout_buf
3259  * Returns:
3260  *              BE_SUCCESS - The command ran successfully without returning
3261  *                      errors.
3262  *              BE_ERR_EXTCMD
3263  *                      - The command could not be run.
3264  *                      - The command terminated with error status.
3265  *                      - There were errors extracting or returning subprocess
3266  *                              data.
3267  *              BE_ERR_NOMEM - The command exceeds the command buffer size.
3268  *              BE_ERR_INVAL - An invalid argument was specified.
3269  * Scope:
3270  *              Semi-private (library wide use only)
3271  */
3272 int
3273 be_run_cmd(char *command, char *stderr_buf, int stderr_bufsize,
3274     char *stdout_buf, int stdout_bufsize)
3275 {
3276         char *temp_filename = strdup(tmpnam(NULL));
3277         FILE *stdout_str = NULL;
3278         FILE *stderr_str = NULL;
3279         char cmdline[BUFSIZ];
3280         char oneline[BUFSIZ];
3281         int exit_status;
3282         int rval = BE_SUCCESS;
3283 
3284         if ((command == NULL) || (stderr_buf == NULL) ||
3285             (stderr_bufsize <= 0) || (stdout_bufsize <  0) ||
3286             ((stdout_buf != NULL) ^ (stdout_bufsize != 0))) {
3287                 return (BE_ERR_INVAL);
3288         }
3289 
3290         /* Set up command so popen returns stderr, not stdout */
3291         if (snprintf(cmdline, BUFSIZ, "%s 2> %s", command,
3292             temp_filename) >= BUFSIZ) {
3293                 rval = BE_ERR_NOMEM;
3294                 goto cleanup;
3295         }
3296 
3297         /* Set up the fifo that will make stderr available. */
3298         if (mkfifo(temp_filename, 0600) != 0) {
3299                 (void) be_print_err(gettext("be_run_cmd: mkfifo: %s\n"),
3300                     strerror(errno));
3301                 rval = BE_ERR_EXTCMD;
3302                 goto cleanup;
3303         }
3304 
3305         if ((stdout_str = popen(cmdline, "r")) == NULL) {
3306                 (void) be_print_err(gettext("be_run_cmd: popen: %s\n"),
3307                     strerror(errno));
3308                 rval = BE_ERR_EXTCMD;
3309                 goto cleanup;
3310         }
3311 
3312         if ((stderr_str = fopen(temp_filename, "r")) == NULL) {
3313                 (void) be_print_err(gettext("be_run_cmd: fopen: %s\n"),
3314                     strerror(errno));
3315                 (void) pclose(stdout_str);
3316                 rval = BE_ERR_EXTCMD;
3317                 goto cleanup;
3318         }
3319 
3320         /* Read stdout first, as it usually outputs more than stderr. */
3321         oneline[BUFSIZ-1] = '\0';
3322         while (fgets(oneline, BUFSIZ-1, stdout_str) != NULL) {
3323                 if (stdout_str != NULL) {
3324                         (void) strlcat(stdout_buf, oneline, stdout_bufsize);
3325                 }
3326         }
3327 
3328         while (fgets(oneline, BUFSIZ-1, stderr_str) != NULL) {
3329                 (void) strlcat(stderr_buf, oneline, stderr_bufsize);
3330         }
3331 
3332         /* Close pipe, get exit status. */
3333         if ((exit_status = pclose(stdout_str)) == -1) {
3334                 (void) be_print_err(gettext("be_run_cmd: pclose: %s\n"),
3335                     strerror(errno));
3336                 rval = BE_ERR_EXTCMD;
3337         } else if (WIFEXITED(exit_status)) {
3338                 exit_status = (int)((char)WEXITSTATUS(exit_status));
3339                 /*
3340                  * error code BC_NOUPDT means more recent version
3341                  * is installed
3342                  */
3343                 if (exit_status != BC_SUCCESS && exit_status != BC_NOUPDT) {
3344                         (void) snprintf(oneline, BUFSIZ, gettext("be_run_cmd: "
3345                             "command terminated with error status: %d\n"),
3346                             exit_status);
3347                         (void) strlcat(stderr_buf, oneline, stderr_bufsize);
3348                         rval = BE_ERR_EXTCMD;
3349                 }
3350         } else {
3351                 (void) snprintf(oneline, BUFSIZ, gettext("be_run_cmd: command "
3352                     "terminated on signal: %s\n"),
3353                     strsignal(WTERMSIG(exit_status)));
3354                 (void) strlcat(stderr_buf, oneline, stderr_bufsize);
3355                 rval = BE_ERR_EXTCMD;
3356         }
3357 
3358 cleanup:
3359         (void) unlink(temp_filename);
3360         (void) free(temp_filename);
3361 
3362         return (rval);
3363 }
3364 
3365 /* ******************************************************************** */
3366 /*                      Private Functions                               */
3367 /* ******************************************************************** */
3368 
3369 /*
3370  * Function:    update_dataset
3371  * Description: This function takes a dataset name and replaces the zpool
3372  *              and be_name components of the dataset with the new be_name
3373  *              zpool passed in.
3374  * Parameters:
3375  *              dataset - name of dataset
3376  *              dataset_len - lenth of buffer in which dataset is passed in.
3377  *              be_name - name of new BE name to update to.
3378  *              old_rc_loc - dataset under which the root container dataset
3379  *                      for the old BE lives.
3380  *              new_rc_loc - dataset under which the root container dataset
3381  *                      for the new BE lives.
3382  * Returns:
3383  *              BE_SUCCESS - Success
3384  *              be_errno_t - Failure
3385  * Scope:
3386  *              Private
3387  */
3388 static int
3389 update_dataset(char *dataset, int dataset_len, char *be_name,
3390     char *old_rc_loc, char *new_rc_loc)
3391 {
3392         char    *ds = NULL;
3393         char    *sub_ds = NULL;
3394 
3395         /* Tear off the BE container dataset */
3396         if ((ds = be_make_name_from_ds(dataset, old_rc_loc)) == NULL) {
3397                 return (BE_ERR_INVAL);
3398         }
3399 
3400         /* Get dataset name relative to BE root, if there is one */
3401         sub_ds = strchr(ds, '/');
3402 
3403         /* Generate the BE root dataset name */
3404         be_make_root_ds(new_rc_loc, be_name, dataset, dataset_len);
3405 
3406         /* If a subordinate dataset name was found, append it */
3407         if (sub_ds != NULL)
3408                 (void) strlcat(dataset, sub_ds, dataset_len);
3409 
3410         free(ds);
3411         return (BE_SUCCESS);
3412 }
3413 
3414 /*
3415  * Function:    _update_vfstab
3416  * Description: This function updates a vfstab file to reflect the new
3417  *              root container dataset location and be_name for all
3418  *              entries listed in the be_fs_list_data_t structure passed in.
3419  * Parameters:
3420  *              vfstab - vfstab file to modify
3421  *              be_name - name of BE to update.
3422  *              old_rc_loc - dataset under which the root container dataset
3423  *                      of the old BE resides in.
3424  *              new_rc_loc - dataset under which the root container dataset
3425  *                      of the new BE resides in.
3426  *              fld - be_fs_list_data_t pointer providing the list of
3427  *                      file systems to look for in vfstab.
3428  * Returns:
3429  *              BE_SUCCESS - Success
3430  *              be_errno_t - Failure
3431  * Scope:
3432  *              Private
3433  */
3434 static int
3435 _update_vfstab(char *vfstab, char *be_name, char *old_rc_loc,
3436     char *new_rc_loc, be_fs_list_data_t *fld)
3437 {
3438         struct vfstab   vp;
3439         char            *tmp_vfstab = NULL;
3440         char            comments_buf[BUFSIZ];
3441         FILE            *comments = NULL;
3442         FILE            *vfs_ents = NULL;
3443         FILE            *tfile = NULL;
3444         struct stat     sb;
3445         char            dev[MAXPATHLEN];
3446         char            *c;
3447         int             fd;
3448         int             ret = BE_SUCCESS, err = 0;
3449         int             i;
3450         int             tmp_vfstab_len = 0;
3451 
3452         errno = 0;
3453 
3454         /*
3455          * Open vfstab for reading twice.  First is for comments,
3456          * second is for actual entries.
3457          */
3458         if ((comments = fopen(vfstab, "r")) == NULL ||
3459             (vfs_ents = fopen(vfstab, "r")) == NULL) {
3460                 err = errno;
3461                 be_print_err(gettext("_update_vfstab: "
3462                     "failed to open vfstab (%s): %s\n"), vfstab,
3463                     strerror(err));
3464                 ret = errno_to_be_err(err);
3465                 goto cleanup;
3466         }
3467 
3468         /* Grab the stats of the original vfstab file */
3469         if (stat(vfstab, &sb) != 0) {
3470                 err = errno;
3471                 be_print_err(gettext("_update_vfstab: "
3472                     "failed to stat file %s: %s\n"), vfstab,
3473                     strerror(err));
3474                 ret = errno_to_be_err(err);
3475                 goto cleanup;
3476         }
3477 
3478         /* Create tmp file for modified vfstab */
3479         if ((tmp_vfstab = (char *)malloc(strlen(vfstab) + 7))
3480             == NULL) {
3481                 be_print_err(gettext("_update_vfstab: "
3482                     "malloc failed\n"));
3483                 ret = BE_ERR_NOMEM;
3484                 goto cleanup;
3485         }
3486         tmp_vfstab_len = strlen(vfstab) + 7;
3487         (void) memset(tmp_vfstab, 0, tmp_vfstab_len);
3488         (void) strlcpy(tmp_vfstab, vfstab, tmp_vfstab_len);
3489         (void) strlcat(tmp_vfstab, "XXXXXX", tmp_vfstab_len);
3490         if ((fd = mkstemp(tmp_vfstab)) == -1) {
3491                 err = errno;
3492                 be_print_err(gettext("_update_vfstab: "
3493                     "mkstemp failed: %s\n"), strerror(err));
3494                 ret = errno_to_be_err(err);
3495                 goto cleanup;
3496         }
3497         if ((tfile = fdopen(fd, "w")) == NULL) {
3498                 err = errno;
3499                 be_print_err(gettext("_update_vfstab: "
3500                     "could not open file for write\n"));
3501                 (void) close(fd);
3502                 ret = errno_to_be_err(err);
3503                 goto cleanup;
3504         }
3505 
3506         while (fgets(comments_buf, BUFSIZ, comments)) {
3507                 for (c = comments_buf; *c != '\0' && isspace(*c); c++)
3508                         ;
3509                 if (*c == '\0') {
3510                         continue;
3511                 } else if (*c == '#') {
3512                         /*
3513                          * If line is a comment line, just put
3514                          * it through to the tmp vfstab.
3515                          */
3516                         (void) fputs(comments_buf, tfile);
3517                 } else {
3518                         /*
3519                          * Else line is a vfstab entry, grab it
3520                          * into a vfstab struct.
3521                          */
3522                         if (getvfsent(vfs_ents, &vp) != 0) {
3523                                 err = errno;
3524                                 be_print_err(gettext("_update_vfstab: "
3525                                     "getvfsent failed: %s\n"), strerror(err));
3526                                 ret = errno_to_be_err(err);
3527                                 goto cleanup;
3528                         }
3529 
3530                         if (vp.vfs_special == NULL || vp.vfs_mountp == NULL) {
3531                                 (void) putvfsent(tfile, &vp);
3532                                 continue;
3533                         }
3534 
3535                         /*
3536                          * If the entry is one of the entries in the list
3537                          * of file systems to update, modify it's device
3538                          * field to be correct for this BE.
3539                          */
3540                         for (i = 0; i < fld->fs_num; i++) {
3541                                 if (strcmp(vp.vfs_special, fld->fs_list[i])
3542                                     == 0) {
3543                                         /*
3544                                          * Found entry that needs an update.
3545                                          * Replace the root container dataset
3546                                          * location and be_name in the
3547                                          * entry's device.
3548                                          */
3549                                         (void) strlcpy(dev, vp.vfs_special,
3550                                             sizeof (dev));
3551 
3552                                         if ((ret = update_dataset(dev,
3553                                             sizeof (dev), be_name, old_rc_loc,
3554                                             new_rc_loc)) != 0) {
3555                                                 be_print_err(
3556                                                     gettext("_update_vfstab: "
3557                                                     "Failed to update device "
3558                                                     "field for vfstab entry "
3559                                                     "%s\n"), fld->fs_list[i]);
3560                                                 goto cleanup;
3561                                         }
3562 
3563                                         vp.vfs_special = dev;
3564                                         break;
3565                                 }
3566                         }
3567 
3568                         /* Put entry through to tmp vfstab */
3569                         (void) putvfsent(tfile, &vp);
3570                 }
3571         }
3572 
3573         (void) fclose(comments);
3574         comments = NULL;
3575         (void) fclose(vfs_ents);
3576         vfs_ents = NULL;
3577         (void) fclose(tfile);
3578         tfile = NULL;
3579 
3580         /* Copy tmp vfstab into place */
3581         if (rename(tmp_vfstab, vfstab) != 0) {
3582                 err = errno;
3583                 be_print_err(gettext("_update_vfstab: "
3584                     "failed to rename file %s to %s: %s\n"), tmp_vfstab,
3585                     vfstab, strerror(err));
3586                 ret = errno_to_be_err(err);
3587                 goto cleanup;
3588         }
3589 
3590         /* Set the perms and ownership of the updated file */
3591         if (chmod(vfstab, sb.st_mode) != 0) {
3592                 err = errno;
3593                 be_print_err(gettext("_update_vfstab: "
3594                     "failed to chmod %s: %s\n"), vfstab, strerror(err));
3595                 ret = errno_to_be_err(err);
3596                 goto cleanup;
3597         }
3598         if (chown(vfstab, sb.st_uid, sb.st_gid) != 0) {
3599                 err = errno;
3600                 be_print_err(gettext("_update_vfstab: "
3601                     "failed to chown %s: %s\n"), vfstab, strerror(err));
3602                 ret = errno_to_be_err(err);
3603                 goto cleanup;
3604         }
3605 
3606 cleanup:
3607         if (comments != NULL)
3608                 (void) fclose(comments);
3609         if (vfs_ents != NULL)
3610                 (void) fclose(vfs_ents);
3611         (void) unlink(tmp_vfstab);
3612         (void) free(tmp_vfstab);
3613         if (tfile != NULL)
3614                 (void) fclose(tfile);
3615 
3616         return (ret);
3617 }
3618 
3619 
3620 /*
3621  * Function:    be_get_auto_name
3622  * Description: Generate an auto name constructed based on the BE name
3623  *              of the original BE or zone BE being cloned.
3624  * Parameters:
3625  *              obe_name - name of the original BE or zone BE being cloned.
3626  *              container_ds - container dataset for the zone.
3627  *                             Note: if zone_be is false this should be
3628  *                                  NULL.
3629  *              zone_be - flag that indicates if we are operating on a zone BE.
3630  * Returns:
3631  *              Success - pointer to auto generated BE name.  The name
3632  *                      is allocated in heap storage so the caller is
3633  *                      responsible for free'ing the name.
3634  *              Failure - NULL
3635  * Scope:
3636  *              Private
3637  */
3638 static char *
3639 be_get_auto_name(char *obe_name, char *be_container_ds, boolean_t zone_be)
3640 {
3641         be_node_list_t  *be_nodes = NULL;
3642         be_node_list_t  *cur_be = NULL;
3643         char            auto_be_name[MAXPATHLEN];
3644         char            base_be_name[MAXPATHLEN];
3645         char            cur_be_name[MAXPATHLEN];
3646         char            *num_str = NULL;
3647         char            *c = NULL;
3648         int             num = 0;
3649         int             cur_num = 0;
3650 
3651         errno = 0;
3652 
3653         /*
3654          * Check if obe_name is already in an auto BE name format.
3655          * If it is, then strip off the increment number to get the
3656          * base name.
3657          */
3658         (void) strlcpy(base_be_name, obe_name, sizeof (base_be_name));
3659 
3660         if ((num_str = strrchr(base_be_name, BE_AUTO_NAME_DELIM))
3661             != NULL) {
3662                 /* Make sure remaining string is all digits */
3663                 c = num_str + 1;
3664                 while (c[0] != '\0' && isdigit(c[0]))
3665                         c++;
3666                 /*
3667                  * If we're now at the end of the string strip off the
3668                  * increment number.
3669                  */
3670                 if (c[0] == '\0')
3671                         num_str[0] = '\0';
3672         }
3673 
3674         if (zone_be) {
3675                 if (be_container_ds == NULL)
3676                         return (NULL);
3677                 if (be_get_zone_be_list(obe_name, be_container_ds,
3678                     &be_nodes) != BE_SUCCESS) {
3679                         be_print_err(gettext("be_get_auto_name: "
3680                             "be_get_zone_be_list failed\n"));
3681                         return (NULL);
3682                 }
3683         } else if (_be_list(NULL, &be_nodes, BE_LIST_DEFAULT) != BE_SUCCESS) {
3684                 be_print_err(gettext("be_get_auto_name: be_list failed\n"));
3685                 return (NULL);
3686         }
3687 
3688         for (cur_be = be_nodes; cur_be != NULL; cur_be = cur_be->be_next_node) {
3689                 (void) strlcpy(cur_be_name, cur_be->be_node_name,
3690                     sizeof (cur_be_name));
3691 
3692                 /* If cur_be_name doesn't match at least base be name, skip. */
3693                 if (strncmp(cur_be_name, base_be_name, strlen(base_be_name))
3694                     != 0)
3695                         continue;
3696 
3697                 /* Get the string following the base be name */
3698                 num_str = cur_be_name + strlen(base_be_name);
3699 
3700                 /*
3701                  * If nothing follows the base be name, this cur_be_name
3702                  * is the BE named with the base be name, skip.
3703                  */
3704                 if (num_str == NULL || num_str[0] == '\0')
3705                         continue;
3706 
3707                 /*
3708                  * Remove the name delimiter.  If its not there,
3709                  * cur_be_name isn't part of this BE name stream, skip.
3710                  */
3711                 if (num_str[0] == BE_AUTO_NAME_DELIM)
3712                         num_str++;
3713                 else
3714                         continue;
3715 
3716                 /* Make sure remaining string is all digits */
3717                 c = num_str;
3718                 while (c[0] != '\0' && isdigit(c[0]))
3719                         c++;
3720                 if (c[0] != '\0')
3721                         continue;
3722 
3723                 /* Convert the number string to an int */
3724                 cur_num = atoi(num_str);
3725 
3726                 /*
3727                  * If failed to convert the string, skip it.  If its too
3728                  * long to be converted to an int, we wouldn't auto generate
3729                  * this number anyway so there couldn't be a conflict.
3730                  * We treat it as a manually created BE name.
3731                  */
3732                 if (cur_num == 0 && errno == EINVAL)
3733                         continue;
3734 
3735                 /*
3736                  * Compare current number to current max number,
3737                  * take higher of the two.
3738                  */
3739                 if (cur_num > num)
3740                         num = cur_num;
3741         }
3742 
3743         /*
3744          * Store off a copy of 'num' incase we need it later.  If incrementing
3745          * 'num' causes it to roll over, this means 'num' is the largest
3746          * positive int possible; we'll need it later in the loop to determine
3747          * if we've exhausted all possible increment numbers.  We store it in
3748          * 'cur_num'.
3749          */
3750         cur_num = num;
3751 
3752         /* Increment 'num' to get new auto BE name number */
3753         if (++num <= 0) {
3754                 int ret = 0;
3755 
3756                 /*
3757                  * Since incrementing 'num' caused it to rollover, start
3758                  * over at 0 and find the first available number.
3759                  */
3760                 for (num = 0; num < cur_num; num++) {
3761 
3762                         (void) snprintf(cur_be_name, sizeof (cur_be_name),
3763                             "%s%c%d", base_be_name, BE_AUTO_NAME_DELIM, num);
3764 
3765                         ret = zpool_iter(g_zfs, be_exists_callback,
3766                             cur_be_name);
3767 
3768                         if (ret == 0) {
3769                                 /*
3770                                  * BE name doesn't exist, break out
3771                                  * to use 'num'.
3772                                  */
3773                                 break;
3774                         } else if (ret == 1) {
3775                                 /* BE name exists, continue looking */
3776                                 continue;
3777                         } else {
3778                                 be_print_err(gettext("be_get_auto_name: "
3779                                     "zpool_iter failed: %s\n"),
3780                                     libzfs_error_description(g_zfs));
3781                                 be_free_list(be_nodes);
3782                                 return (NULL);
3783                         }
3784                 }
3785 
3786                 /*
3787                  * If 'num' equals 'cur_num', we've exhausted all possible
3788                  * auto BE names for this base BE name.
3789                  */
3790                 if (num == cur_num) {
3791                         be_print_err(gettext("be_get_auto_name: "
3792                             "No more available auto BE names for base "
3793                             "BE name %s\n"), base_be_name);
3794                         be_free_list(be_nodes);
3795                         return (NULL);
3796                 }
3797         }
3798 
3799         be_free_list(be_nodes);
3800 
3801         /*
3802          * Generate string for auto BE name.
3803          */
3804         (void) snprintf(auto_be_name, sizeof (auto_be_name), "%s%c%d",
3805             base_be_name, BE_AUTO_NAME_DELIM, num);
3806 
3807         if ((c = strdup(auto_be_name)) == NULL) {
3808                 be_print_err(gettext("be_get_auto_name: "
3809                     "memory allocation failed\n"));
3810                 return (NULL);
3811         }
3812 
3813         return (c);
3814 }
3815 
3816 /*
3817  * Function:    be_get_console_prop
3818  * Description: Determine console device.
3819  * Returns:
3820  *              Success - pointer to console setting.
3821  *              Failure - NULL
3822  * Scope:
3823  *              Private
3824  */
3825 static char *
3826 be_get_console_prop(void)
3827 {
3828         di_node_t       dn;
3829         char *console = NULL;
3830 
3831         if ((dn = di_init("/", DINFOPROP)) == DI_NODE_NIL) {
3832                 be_print_err(gettext("be_get_console_prop: "
3833                     "di_init() failed\n"));
3834                 return (NULL);
3835         }
3836 
3837         if (di_prop_lookup_strings(DDI_DEV_T_ANY, dn,
3838             "console", &console) != -1) {
3839                 di_fini(dn);
3840                 return (console);
3841         }
3842 
3843         if (console == NULL) {
3844                 if (di_prop_lookup_strings(DDI_DEV_T_ANY, dn,
3845                     "output-device", &console) != -1) {
3846                         di_fini(dn);
3847                         if (strncmp(console, "screen", strlen("screen")) == 0)
3848                                 console = BE_DEFAULT_CONSOLE;
3849                 }
3850         }
3851 
3852         /*
3853          * Default console to text
3854          */
3855         if (console == NULL) {
3856                 console = BE_DEFAULT_CONSOLE;
3857         }
3858 
3859         return (console);
3860 }
3861 
3862 /*
3863  * Function:    be_create_menu
3864  * Description:
3865  *              This function is used if no menu.lst file exists. In
3866  *              this case a new file is created and if needed default
3867  *              lines are added to the file.
3868  * Parameters:
3869  *              pool - The name of the pool the menu.lst file is on
3870  *              menu_file - The name of the file we're creating.
3871  *              menu_fp - A pointer to the file pointer of the file we
3872  *                        created. This is also used to pass back the file
3873  *                        pointer to the newly created file.
3874  *              mode - the original mode used for the failed attempt to
3875  *                     non-existent file.
3876  * Returns:
3877  *              BE_SUCCESS - Success
3878  *              be_errno_t - Failure
3879  * Scope:
3880  *              Private
3881  */
3882 static int
3883 be_create_menu(
3884         char *pool,
3885         char *menu_file,
3886         FILE **menu_fp,
3887         char *mode)
3888 {
3889         be_node_list_t  *be_nodes = NULL;
3890         char *menu_path = NULL;
3891         char *be_rpool = NULL;
3892         char *be_name = NULL;
3893         char *console = NULL;
3894         errno = 0;
3895 
3896         if (menu_file == NULL || menu_fp == NULL || mode == NULL)
3897                 return (BE_ERR_INVAL);
3898 
3899         menu_path = strdup(menu_file);
3900         if (menu_path == NULL)
3901                 return (BE_ERR_NOMEM);
3902 
3903         (void) dirname(menu_path);
3904         if (*menu_path == '.') {
3905                 free(menu_path);
3906                 return (BE_ERR_BAD_MENU_PATH);
3907         }
3908         if (mkdirp(menu_path,
3909             S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == -1 &&
3910             errno != EEXIST) {
3911                 free(menu_path);
3912                 be_print_err(gettext("be_create_menu: Failed to create the %s "
3913                     "directory: %s\n"), menu_path, strerror(errno));
3914                 return (errno_to_be_err(errno));
3915         }
3916         free(menu_path);
3917 
3918         /*
3919          * Check to see if this system supports grub
3920          */
3921         if (be_has_grub()) {
3922                 /*
3923                  * The grub menu is missing so we need to create it
3924                  * and fill in the first few lines.
3925                  */
3926                 FILE *temp_fp = fopen(menu_file, "a+");
3927                 if (temp_fp == NULL) {
3928                         *menu_fp = NULL;
3929                         return (errno_to_be_err(errno));
3930                 }
3931 
3932                 if ((console = be_get_console_prop()) != NULL) {
3933 
3934                         /*
3935                          * If console is redirected to serial line,
3936                          * GRUB splash screen will not be enabled.
3937                          */
3938                         if (strncmp(console, "text", strlen("text")) == 0 ||
3939                             strncmp(console, "graphics",
3940                             strlen("graphics")) == 0) {
3941 
3942                                 (void) fprintf(temp_fp, "%s\n", BE_GRUB_SPLASH);
3943                                 (void) fprintf(temp_fp, "%s\n",
3944                                     BE_GRUB_FOREGROUND);
3945                                 (void) fprintf(temp_fp, "%s\n",
3946                                     BE_GRUB_BACKGROUND);
3947                                 (void) fprintf(temp_fp, "%s\n",
3948                                     BE_GRUB_DEFAULT);
3949                         } else {
3950                                 be_print_err(gettext("be_create_menu: "
3951                                     "console on serial line, "
3952                                     "GRUB splash image will be disabled\n"));
3953                         }
3954                 }
3955 
3956                 (void) fprintf(temp_fp, "timeout 30\n");
3957                 (void) fclose(temp_fp);
3958 
3959         } else {
3960                 /*
3961                  * The menu file doesn't exist so we need to create a
3962                  * blank file.
3963                  */
3964                 FILE *temp_fp = fopen(menu_file, "w+");
3965                 if (temp_fp == NULL) {
3966                         *menu_fp = NULL;
3967                         return (errno_to_be_err(errno));
3968                 }
3969                 (void) fclose(temp_fp);
3970         }
3971 
3972         /*
3973          * Now we need to add all the BE's back into the the file.
3974          */
3975         if (_be_list(NULL, &be_nodes, BE_LIST_DEFAULT) == BE_SUCCESS) {
3976                 while (be_nodes != NULL) {
3977                         if (strcmp(pool, be_nodes->be_rpool) == 0) {
3978                                 (void) be_append_menu(be_nodes->be_node_name,
3979                                     be_nodes->be_rpool, NULL, NULL, NULL);
3980                         }
3981                         if (be_nodes->be_active_on_boot) {
3982                                 be_rpool = strdup(be_nodes->be_rpool);
3983                                 be_name = strdup(be_nodes->be_node_name);
3984                         }
3985 
3986                         be_nodes = be_nodes->be_next_node;
3987                 }
3988         }
3989         be_free_list(be_nodes);
3990 
3991         /*
3992          * Check to see if this system supports grub
3993          */
3994         if (be_has_grub()) {
3995                 int err = be_change_grub_default(be_name, be_rpool);
3996                 if (err != BE_SUCCESS)
3997                         return (err);
3998         }
3999         *menu_fp = fopen(menu_file, mode);
4000         if (*menu_fp == NULL)
4001                 return (errno_to_be_err(errno));
4002 
4003         return (BE_SUCCESS);
4004 }
4005 
4006 /*
4007  * Function:    be_open_menu
4008  * Description:
4009  *              This function is used it open the menu.lst file. If this
4010  *              file does not exist be_create_menu is called to create it
4011  *              and the open file pointer is returned. If the file does
4012  *              exist it is simply opened using the mode passed in.
4013  * Parameters:
4014  *              pool - The name of the pool the menu.lst file is on
4015  *              menu_file - The name of the file we're opening.
4016  *              menu_fp - A pointer to the file pointer of the file we're
4017  *                        opening. This is also used to pass back the file
4018  *                        pointer.
4019  *              mode - the original mode to be used for opening the menu.lst
4020  *                     file.
4021  *              create_menu - If this is true and the menu.lst file does not
4022  *                            exist we will attempt to re-create it. However
4023  *                            if it's false the error returned from the fopen
4024  *                            will be returned.
4025  * Returns:
4026  *              BE_SUCCESS - Success
4027  *              be_errno_t - Failure
4028  * Scope:
4029  *              Private
4030  */
4031 static int
4032 be_open_menu(
4033         char *pool,
4034         char *menu_file,
4035         FILE **menu_fp,
4036         char *mode,
4037         boolean_t create_menu)
4038 {
4039         int     err = 0;
4040         boolean_t       set_print = B_FALSE;
4041 
4042         *menu_fp = fopen(menu_file, mode);
4043         err = errno;
4044         if (*menu_fp == NULL) {
4045                 if (err == ENOENT && create_menu) {
4046                         be_print_err(gettext("be_open_menu: menu.lst "
4047                             "file %s does not exist,\n"), menu_file);
4048                         if (!do_print) {
4049                                 set_print = B_TRUE;
4050                                 do_print = B_TRUE;
4051                         }
4052                         be_print_err(gettext("WARNING: menu.lst "
4053                             "file %s does not exist,\n         generating "
4054                             "a new menu.lst file\n"), menu_file);
4055                         if (set_print)
4056                                 do_print = B_FALSE;
4057                         err = 0;
4058                         if ((err = be_create_menu(pool, menu_file,
4059                             menu_fp, mode)) == ENOENT)
4060                                 return (BE_ERR_NO_MENU);
4061                         else if (err != BE_SUCCESS)
4062                                 return (err);
4063                         else if (*menu_fp == NULL)
4064                                 return (BE_ERR_NO_MENU);
4065                 } else {
4066                         be_print_err(gettext("be_open_menu: failed "
4067                             "to open menu.lst file %s\n"), menu_file);
4068                         if (err == ENOENT)
4069                                 return (BE_ERR_NO_MENU);
4070                         else
4071                                 return (errno_to_be_err(err));
4072                 }
4073         }
4074         return (BE_SUCCESS);
4075 }