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