summaryrefslogtreecommitdiff
path: root/usr/src/lib/libbe/common/be_activate.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/libbe/common/be_activate.c')
-rw-r--r--usr/src/lib/libbe/common/be_activate.c1191
1 files changed, 1191 insertions, 0 deletions
diff --git a/usr/src/lib/libbe/common/be_activate.c b/usr/src/lib/libbe/common/be_activate.c
new file mode 100644
index 0000000000..c83e442fa9
--- /dev/null
+++ b/usr/src/lib/libbe/common/be_activate.c
@@ -0,0 +1,1191 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ */
+
+#include <assert.h>
+#include <libintl.h>
+#include <libnvpair.h>
+#include <libzfs.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/mnttab.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <libbe.h>
+#include <libbe_priv.h>
+
+char *mnttab = MNTTAB;
+
+/*
+ * Private function prototypes
+ */
+static int set_bootfs(char *boot_rpool, char *be_root_ds);
+static int set_canmount(be_node_list_t *, char *);
+static int be_do_installgrub(be_transaction_data_t *);
+static int be_get_grub_vers(be_transaction_data_t *, char **, char **);
+static int get_ver_from_capfile(char *, char **);
+static int be_promote_zone_ds(char *, char *);
+static int be_promote_ds_callback(zfs_handle_t *, void *);
+
+/* ******************************************************************** */
+/* Public Functions */
+/* ******************************************************************** */
+
+/*
+ * Function: be_activate
+ * Description: Calls _be_activate which activates the BE named in the
+ * attributes passed in through be_attrs. The process of
+ * activation sets the bootfs property of the root pool, resets
+ * the canmount property to noauto, and sets the default in the
+ * grub menu to the entry corresponding to the entry for the named
+ * BE.
+ * Parameters:
+ * be_attrs - pointer to nvlist_t of attributes being passed in.
+ * The follow attribute values are used by this function:
+ *
+ * BE_ATTR_ORIG_BE_NAME *required
+ * Return:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Public
+ */
+int
+be_activate(nvlist_t *be_attrs)
+{
+ int ret = BE_SUCCESS;
+ char *be_name = NULL;
+
+ /* Initialize libzfs handle */
+ if (!be_zfs_init())
+ return (BE_ERR_INIT);
+
+ /* Get the BE name to activate */
+ if (nvlist_lookup_string(be_attrs, BE_ATTR_ORIG_BE_NAME, &be_name)
+ != 0) {
+ be_print_err(gettext("be_activate: failed to "
+ "lookup BE_ATTR_ORIG_BE_NAME attribute\n"));
+ be_zfs_fini();
+ return (BE_ERR_INVAL);
+ }
+
+ /* Validate BE name */
+ if (!be_valid_be_name(be_name)) {
+ be_print_err(gettext("be_activate: invalid BE name %s\n"),
+ be_name);
+ be_zfs_fini();
+ return (BE_ERR_INVAL);
+ }
+
+ ret = _be_activate(be_name);
+
+ be_zfs_fini();
+
+ return (ret);
+}
+
+/* ******************************************************************** */
+/* Semi Private Functions */
+/* ******************************************************************** */
+
+/*
+ * Function: _be_activate
+ * Description: This does the actual work described in be_activate.
+ * Parameters:
+ * be_name - pointer to the name of BE to activate.
+ *
+ * Return:
+ * BE_SUCCESS - Success
+ * be_errnot_t - Failure
+ * Scope:
+ * Public
+ */
+int
+_be_activate(char *be_name)
+{
+ be_transaction_data_t cb = { 0 };
+ zfs_handle_t *zhp = NULL;
+ char root_ds[MAXPATHLEN];
+ char *cur_vers = NULL, *new_vers = NULL;
+ be_node_list_t *be_nodes = NULL;
+ uuid_t uu = {0};
+ int entry, ret = BE_SUCCESS;
+ int zret = 0;
+
+ /*
+ * TODO: The BE needs to be validated to make sure that it is actually
+ * a bootable BE.
+ */
+
+ if (be_name == NULL)
+ return (BE_ERR_INVAL);
+
+ /* Set obe_name to be_name in the cb structure */
+ cb.obe_name = be_name;
+
+ /* find which zpool the be is in */
+ if ((zret = zpool_iter(g_zfs, be_find_zpool_callback, &cb)) == 0) {
+ be_print_err(gettext("be_activate: failed to "
+ "find zpool for BE (%s)\n"), cb.obe_name);
+ return (BE_ERR_BE_NOENT);
+ } else if (zret < 0) {
+ be_print_err(gettext("be_activate: "
+ "zpool_iter failed: %s\n"),
+ libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ return (ret);
+ }
+
+ be_make_root_ds(cb.obe_zpool, cb.obe_name, root_ds, sizeof (root_ds));
+ cb.obe_root_ds = strdup(root_ds);
+
+ if (getzoneid() == GLOBAL_ZONEID) {
+ if (be_has_grub() && (ret = be_get_grub_vers(&cb, &cur_vers,
+ &new_vers)) != BE_SUCCESS) {
+ be_print_err(gettext("be_activate: failed to get grub "
+ "versions from capability files.\n"));
+ return (ret);
+ }
+ if (cur_vers != NULL) {
+ /*
+ * We need to check to see if the version number from
+ * the BE being activated is greater than the current
+ * one.
+ */
+ if (new_vers != NULL &&
+ atof(cur_vers) < atof(new_vers)) {
+ if ((ret = be_do_installgrub(&cb))
+ != BE_SUCCESS) {
+ free(new_vers);
+ free(cur_vers);
+ return (ret);
+ }
+ free(new_vers);
+ }
+ free(cur_vers);
+ } else if (new_vers != NULL) {
+ if ((ret = be_do_installgrub(&cb)) != BE_SUCCESS) {
+ free(new_vers);
+ return (ret);
+ }
+ free(new_vers);
+ }
+ if (!be_has_menu_entry(root_ds, cb.obe_zpool, &entry)) {
+ if ((ret = be_append_menu(cb.obe_name, cb.obe_zpool,
+ NULL, NULL, NULL)) != BE_SUCCESS) {
+ be_print_err(gettext("be_activate: Failed to "
+ "add BE (%s) to the GRUB menu\n"),
+ cb.obe_name);
+ goto done;
+ }
+ }
+ if (be_has_grub()) {
+ if ((ret = be_change_grub_default(cb.obe_name,
+ cb.obe_zpool)) != BE_SUCCESS) {
+ be_print_err(gettext("be_activate: failed to "
+ "change the default entry in menu.lst\n"));
+ goto done;
+ }
+ }
+ }
+
+ if ((ret = _be_list(cb.obe_name, &be_nodes)) != BE_SUCCESS) {
+ return (ret);
+ }
+
+ if ((ret = set_canmount(be_nodes, "noauto")) != BE_SUCCESS) {
+ be_print_err(gettext("be_activate: failed to set "
+ "canmount dataset property\n"));
+ goto done;
+ }
+
+ if ((ret = set_bootfs(be_nodes->be_rpool, root_ds)) != BE_SUCCESS) {
+ be_print_err(gettext("be_activate: failed to set "
+ "bootfs pool property for %s\n"), root_ds);
+ goto done;
+ }
+
+ if ((zhp = zfs_open(g_zfs, root_ds, ZFS_TYPE_FILESYSTEM)) != NULL) {
+ /*
+ * We don't need to close the zfs handle at this
+ * point because The callback funtion
+ * be_promote_ds_callback() will close it for us.
+ */
+ if (be_promote_ds_callback(zhp, NULL) != 0) {
+ be_print_err(gettext("be_activate: "
+ "failed to activate the "
+ "datasets for %s: %s\n"),
+ root_ds,
+ libzfs_error_description(g_zfs));
+ ret = BE_ERR_PROMOTE;
+ goto done;
+ }
+ } else {
+ be_print_err(gettext("be_activate:: failed to open "
+ "dataset (%s): %s\n"), root_ds,
+ libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ goto done;
+ }
+
+ if (getzoneid() == GLOBAL_ZONEID &&
+ be_get_uuid(cb.obe_root_ds, &uu) == BE_SUCCESS &&
+ (ret = be_promote_zone_ds(cb.obe_name, cb.obe_root_ds))
+ != BE_SUCCESS) {
+ be_print_err(gettext("be_activate: failed to promote "
+ "the active zonepath datasets for zones in BE %s\n"),
+ cb.obe_name);
+ }
+
+done:
+ be_free_list(be_nodes);
+ return (ret);
+}
+
+/*
+ * Function: be_activate_current_be
+ * Description: Set the currently "active" BE to be "active on boot"
+ * Paramters:
+ * none
+ * Returns:
+ * BE_SUCCESS - Success
+ * be_errnot_t - Failure
+ * Scope:
+ * Semi-private (library wide use only)
+ */
+int
+be_activate_current_be(void)
+{
+ int ret = BE_SUCCESS;
+ be_transaction_data_t bt = { 0 };
+
+ if ((ret = be_find_current_be(&bt)) != BE_SUCCESS) {
+ return (ret);
+ }
+
+ if ((ret = _be_activate(bt.obe_name)) != BE_SUCCESS) {
+ be_print_err(gettext("be_activate_current_be: failed to "
+ "activate %s\n"), bt.obe_name);
+ return (ret);
+ }
+
+ return (BE_SUCCESS);
+}
+
+/*
+ * Function: be_is_active_on_boot
+ * Description: Checks if the BE name passed in has the "active on boot"
+ * property set to B_TRUE.
+ * Paramters:
+ * be_name - the name of the BE to check
+ * Returns:
+ * B_TRUE - if active on boot.
+ * B_FALSE - if not active on boot.
+ * Scope:
+ * Semi-private (library wide use only)
+ */
+boolean_t
+be_is_active_on_boot(char *be_name)
+{
+ be_node_list_t *be_node = NULL;
+
+ if (be_name == NULL) {
+ be_print_err(gettext("be_is_active_on_boot: "
+ "be_name must not be NULL\n"));
+ return (B_FALSE);
+ }
+
+ if (_be_list(be_name, &be_node) != BE_SUCCESS) {
+ return (B_FALSE);
+ }
+
+ if (be_node == NULL) {
+ return (B_FALSE);
+ }
+
+ if (be_node->be_active_on_boot) {
+ be_free_list(be_node);
+ return (B_TRUE);
+ } else {
+ be_free_list(be_node);
+ return (B_FALSE);
+ }
+}
+
+/* ******************************************************************** */
+/* Private Functions */
+/* ******************************************************************** */
+
+/*
+ * Function: set_bootfs
+ * Description: Sets the bootfs property on the boot pool to be the
+ * root dataset of the activated BE.
+ * Parameters:
+ * boot_pool - The pool we're setting bootfs in.
+ * be_root_ds - The main dataset for the BE.
+ * Return:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Private
+ */
+static int
+set_bootfs(char *boot_rpool, char *be_root_ds)
+{
+ zpool_handle_t *zhp;
+ int err = BE_SUCCESS;
+
+ if ((zhp = zpool_open(g_zfs, boot_rpool)) == NULL) {
+ be_print_err(gettext("set_bootfs: failed to open pool "
+ "(%s): %s\n"), boot_rpool, libzfs_error_description(g_zfs));
+ err = zfs_err_to_be_err(g_zfs);
+ return (err);
+ }
+
+ err = zpool_set_prop(zhp, "bootfs", be_root_ds);
+ if (err) {
+ be_print_err(gettext("set_bootfs: failed to set "
+ "bootfs property for pool %s: %s\n"), boot_rpool,
+ libzfs_error_description(g_zfs));
+ err = zfs_err_to_be_err(g_zfs);
+ zpool_close(zhp);
+ return (err);
+ }
+
+ zpool_close(zhp);
+ return (BE_SUCCESS);
+}
+
+/*
+ * Function: set_canmount
+ * Description: Sets the canmount property on the datasets of the
+ * activated BE.
+ * Parameters:
+ * be_nodes - The be_node_t returned from be_list
+ * value - The value of canmount we setting, on|off|noauto.
+ * Return:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Private
+ */
+static int
+set_canmount(be_node_list_t *be_nodes, char *value)
+{
+ char ds_path[MAXPATHLEN];
+ zfs_handle_t *zhp = NULL;
+ be_node_list_t *list = be_nodes;
+ int err = BE_SUCCESS;
+
+ while (list != NULL) {
+ be_dataset_list_t *datasets = list->be_node_datasets;
+
+ be_make_root_ds(list->be_rpool, list->be_node_name, ds_path,
+ sizeof (ds_path));
+
+ if ((zhp = zfs_open(g_zfs, ds_path, ZFS_TYPE_DATASET)) ==
+ NULL) {
+ be_print_err(gettext("set_canmount: failed to open "
+ "dataset (%s): %s\n"), ds_path,
+ libzfs_error_description(g_zfs));
+ err = zfs_err_to_be_err(g_zfs);
+ return (err);
+ }
+ if (zfs_prop_get_int(zhp, ZFS_PROP_MOUNTED)) {
+ /*
+ * it's already mounted so we can't change the
+ * canmount property anyway.
+ */
+ err = BE_SUCCESS;
+ } else {
+ err = zfs_prop_set(zhp,
+ zfs_prop_to_name(ZFS_PROP_CANMOUNT), value);
+ if (err) {
+ ZFS_CLOSE(zhp);
+ be_print_err(gettext("set_canmount: failed to "
+ "set dataset property (%s): %s\n"),
+ ds_path, libzfs_error_description(g_zfs));
+ err = zfs_err_to_be_err(g_zfs);
+ return (err);
+ }
+ }
+ ZFS_CLOSE(zhp);
+
+ while (datasets != NULL) {
+ be_make_root_ds(list->be_rpool,
+ datasets->be_dataset_name, ds_path,
+ sizeof (ds_path));
+
+ if ((zhp = zfs_open(g_zfs, ds_path, ZFS_TYPE_DATASET))
+ == NULL) {
+ be_print_err(gettext("set_canmount: failed to "
+ "open dataset %s: %s\n"), ds_path,
+ libzfs_error_description(g_zfs));
+ err = zfs_err_to_be_err(g_zfs);
+ return (err);
+ }
+ if (zfs_prop_get_int(zhp, ZFS_PROP_MOUNTED)) {
+ /*
+ * it's already mounted so we can't change the
+ * canmount property anyway.
+ */
+ err = BE_SUCCESS;
+ ZFS_CLOSE(zhp);
+ break;
+ }
+ err = zfs_prop_set(zhp,
+ zfs_prop_to_name(ZFS_PROP_CANMOUNT), value);
+ if (err) {
+ ZFS_CLOSE(zhp);
+ be_print_err(gettext("set_canmount: "
+ "Failed to set property value %s "
+ "for dataset %s: %s\n"), value, ds_path,
+ libzfs_error_description(g_zfs));
+ err = zfs_err_to_be_err(g_zfs);
+ return (err);
+ }
+ ZFS_CLOSE(zhp);
+ datasets = datasets->be_next_dataset;
+ }
+ list = list->be_next_node;
+ }
+ return (err);
+}
+
+/*
+ * Function: be_get_grub_vers
+ * Description: Gets the grub version number from /boot/grub/capability. If
+ * capability file doesn't exist NULL is returned.
+ * Parameters:
+ * bt - The transaction data for the BE we're getting the grub
+ * version for.
+ * cur_vers - used to return the current version of grub from
+ * the root pool.
+ * new_vers - used to return the grub version of the BE we're
+ * activating.
+ * Return:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failed to find version
+ * Scope:
+ * Private
+ */
+static int
+be_get_grub_vers(be_transaction_data_t *bt, char **cur_vers, char **new_vers)
+{
+ zfs_handle_t *zhp = NULL;
+ zfs_handle_t *pool_zhp = NULL;
+ int ret = BE_SUCCESS;
+ char cap_file[MAXPATHLEN];
+ char *temp_mntpnt = NULL;
+ char *zpool_mntpt = NULL;
+ char *ptmp_mntpnt = NULL;
+ char *orig_mntpnt = NULL;
+ boolean_t be_mounted = B_FALSE;
+ boolean_t pool_mounted = B_FALSE;
+
+ if (!be_has_grub()) {
+ be_print_err(gettext("be_get_grub_vers: Not supported on "
+ "this architecture\n"));
+ return (BE_ERR_NOTSUP);
+ }
+
+ if (bt == NULL || bt->obe_name == NULL || bt->obe_zpool == NULL ||
+ bt->obe_root_ds == NULL) {
+ be_print_err(gettext("be_get_grub_vers: Invalid BE\n"));
+ return (BE_ERR_INVAL);
+ }
+
+ if ((pool_zhp = zfs_open(g_zfs, bt->obe_zpool, ZFS_TYPE_FILESYSTEM)) ==
+ NULL) {
+ be_print_err(gettext("be_get_grub_vers: zfs_open failed: %s\n"),
+ libzfs_error_description(g_zfs));
+ return (zfs_err_to_be_err(g_zfs));
+ }
+
+ /*
+ * Check to see if the pool's dataset is mounted. If it isn't we'll
+ * attempt to mount it.
+ */
+ if ((ret = be_mount_pool(pool_zhp, &ptmp_mntpnt,
+ &orig_mntpnt, &pool_mounted)) != BE_SUCCESS) {
+ be_print_err(gettext("be_get_grub_vers: pool dataset "
+ "(%s) could not be mounted\n"), bt->obe_zpool);
+ ZFS_CLOSE(pool_zhp);
+ return (ret);
+ }
+
+ /*
+ * Get the mountpoint for the root pool dataset.
+ */
+ if (!zfs_is_mounted(pool_zhp, &zpool_mntpt)) {
+ be_print_err(gettext("be_get_grub_vers: pool "
+ "dataset (%s) is not mounted. Can't set the "
+ "default BE in the grub menu.\n"), bt->obe_zpool);
+ ret = BE_ERR_NO_MENU;
+ goto cleanup;
+ }
+
+ /*
+ * get the version of the most recent grub update.
+ */
+ (void) snprintf(cap_file, sizeof (cap_file), "%s%s",
+ zpool_mntpt, BE_CAP_FILE);
+ free(zpool_mntpt);
+ zpool_mntpt = NULL;
+
+ if ((ret = get_ver_from_capfile(cap_file, cur_vers)) != BE_SUCCESS)
+ goto cleanup;
+
+ if ((zhp = zfs_open(g_zfs, bt->obe_root_ds, ZFS_TYPE_FILESYSTEM)) ==
+ NULL) {
+ be_print_err(gettext("be_get_grub_vers: failed to "
+ "open BE root dataset (%s): %s\n"), bt->obe_root_ds,
+ libzfs_error_description(g_zfs));
+ free(cur_vers);
+ ret = zfs_err_to_be_err(g_zfs);
+ goto cleanup;
+ }
+ if (!zfs_is_mounted(zhp, &temp_mntpnt)) {
+ if ((ret = _be_mount(bt->obe_name, &temp_mntpnt,
+ BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) {
+ be_print_err(gettext("be_get_grub_vers: failed to "
+ "mount BE (%s)\n"), bt->obe_name);
+ free(*cur_vers);
+ *cur_vers = NULL;
+ ZFS_CLOSE(zhp);
+ goto cleanup;
+ }
+ be_mounted = B_TRUE;
+ }
+ ZFS_CLOSE(zhp);
+
+ /*
+ * Now get the grub version for the BE being activated.
+ */
+ (void) snprintf(cap_file, sizeof (cap_file), "%s%s", temp_mntpnt,
+ BE_CAP_FILE);
+ ret = get_ver_from_capfile(cap_file, new_vers);
+ if (ret != BE_SUCCESS) {
+ free(*cur_vers);
+ *cur_vers = NULL;
+ }
+ if (be_mounted)
+ (void) _be_unmount(bt->obe_name, 0);
+
+cleanup:
+ if (pool_mounted) {
+ int iret = BE_SUCCESS;
+ iret = be_unmount_pool(pool_zhp, ptmp_mntpnt, orig_mntpnt);
+ if (ret == BE_SUCCESS)
+ ret = iret;
+ free(orig_mntpnt);
+ free(ptmp_mntpnt);
+ }
+ ZFS_CLOSE(pool_zhp);
+
+ free(temp_mntpnt);
+ return (ret);
+}
+
+/*
+ * Function: get_ver_from_capfile
+ * Description: Parses the capability file passed in looking for the VERSION
+ * line. If found the version is returned in vers, if not then
+ * NULL is returned in vers.
+ *
+ * Parameters:
+ * file - the path to the capability file we want to parse.
+ * vers - the version string that will be passed back.
+ * Return:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failed to find version
+ * Scope:
+ * Private
+ */
+static int
+get_ver_from_capfile(char *file, char **vers)
+{
+ FILE *fp = NULL;
+ char line[BUFSIZ];
+ char *last = NULL;
+ int err = BE_SUCCESS;
+ errno = 0;
+
+ if (!be_has_grub()) {
+ be_print_err(gettext("get_ver_from_capfile: Not supported "
+ "on this architecture\n"));
+ return (BE_ERR_NOTSUP);
+ }
+
+ /*
+ * Set version string to NULL; the only case this shouldn't be set
+ * to be NULL is when we've actually found a version in the capability
+ * file, which is set below.
+ */
+ *vers = NULL;
+
+ /*
+ * If the capability file doesn't exist, we're returning success
+ * because on older releases, the capability file did not exist
+ * so this is a valid scenario.
+ */
+ if (access(file, F_OK) == 0) {
+ if ((fp = fopen(file, "r")) == NULL) {
+ err = errno;
+ be_print_err(gettext("get_ver_from_capfile: failed to "
+ "open file %s with error %s\n"), file,
+ strerror(err));
+ err = errno_to_be_err(err);
+ return (err);
+ }
+
+ while (fgets(line, BUFSIZ, fp)) {
+ char *tok = strtok_r(line, "=", &last);
+
+ if (tok == NULL || tok[0] == '#') {
+ continue;
+ } else if (strcmp(tok, "VERSION") == 0) {
+ *vers = strdup(last);
+ break;
+ }
+ }
+ (void) fclose(fp);
+ }
+
+ return (BE_SUCCESS);
+}
+
+/*
+ * Function: be_do_installgrub
+ * Description: This function runs installgrub using the grub loader files
+ * from the BE we're activating and installing them on the
+ * pool the BE lives in.
+ *
+ * Parameters:
+ * bt - The transaction data for the BE we're activating.
+ * Return:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ *
+ * Scope:
+ * Private
+ */
+static int
+be_do_installgrub(be_transaction_data_t *bt)
+{
+ zpool_handle_t *zphp = NULL;
+ zfs_handle_t *zhp = NULL;
+ nvlist_t **child, *nv, *config;
+ uint_t c, children = 0;
+ char *tmp_mntpt = NULL;
+ char *pool_mntpnt = NULL;
+ char *ptmp_mntpnt = NULL;
+ char *orig_mntpnt = NULL;
+ FILE *cap_fp = NULL;
+ FILE *zpool_cap_fp = NULL;
+ char line[BUFSIZ];
+ char cap_file[MAXPATHLEN];
+ char zpool_cap_file[MAXPATHLEN];
+ char stage1[MAXPATHLEN];
+ char stage2[MAXPATHLEN];
+ char installgrub_cmd[MAXPATHLEN];
+ char *vname;
+ char be_run_cmd_errbuf[BUFSIZ];
+ int ret = BE_SUCCESS;
+ int err = 0;
+ boolean_t be_mounted = B_FALSE;
+ boolean_t pool_mounted = B_FALSE;
+
+ if (!be_has_grub()) {
+ be_print_err(gettext("be_do_installgrub: Not supported "
+ "on this architecture\n"));
+ return (BE_ERR_NOTSUP);
+ }
+
+ if ((zhp = zfs_open(g_zfs, bt->obe_root_ds, ZFS_TYPE_FILESYSTEM)) ==
+ NULL) {
+ be_print_err(gettext("be_do_installgrub: failed to "
+ "open BE root dataset (%s): %s\n"), bt->obe_root_ds,
+ libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ return (ret);
+ }
+ if (!zfs_is_mounted(zhp, &tmp_mntpt)) {
+ if ((ret = _be_mount(bt->obe_name, &tmp_mntpt,
+ BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) {
+ be_print_err(gettext("be_do_installgrub: failed to "
+ "mount BE (%s)\n"), bt->obe_name);
+ ZFS_CLOSE(zhp);
+ return (ret);
+ }
+ be_mounted = B_TRUE;
+ }
+ ZFS_CLOSE(zhp);
+
+ (void) snprintf(stage1, sizeof (stage1), "%s%s", tmp_mntpt, BE_STAGE_1);
+ (void) snprintf(stage2, sizeof (stage2), "%s%s", tmp_mntpt, BE_STAGE_2);
+
+ if ((zphp = zpool_open(g_zfs, bt->obe_zpool)) == NULL) {
+ be_print_err(gettext("be_do_installgrub: failed to open "
+ "pool (%s): %s\n"), bt->obe_zpool,
+ libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ if (be_mounted)
+ (void) _be_unmount(bt->obe_name, 0);
+ free(tmp_mntpt);
+ return (ret);
+ }
+
+ if ((config = zpool_get_config(zphp, NULL)) == NULL) {
+ be_print_err(gettext("be_do_installgrub: failed to get zpool "
+ "configuration information. %s\n"),
+ libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ goto done;
+ }
+
+ /*
+ * Get the vdev tree
+ */
+ if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, &nv) != 0) {
+ be_print_err(gettext("be_do_installgrub: failed to get vdev "
+ "tree: %s\n"), libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ goto done;
+ }
+
+ if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN, &child,
+ &children) != 0) {
+ be_print_err(gettext("be_do_installgrub: failed to traverse "
+ "the vdev tree: %s\n"), libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ goto done;
+ }
+ for (c = 0; c < children; c++) {
+ uint_t i, nchildren = 0;
+ nvlist_t **nvchild;
+ vname = zpool_vdev_name(g_zfs, zphp, child[c], B_FALSE);
+ if (vname == NULL) {
+ be_print_err(gettext(
+ "be_do_installgrub: "
+ "failed to get device name: %s\n"),
+ libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ goto done;
+ }
+ if (strcmp(vname, "mirror") == 0 || vname[0] != 'c') {
+
+ if (nvlist_lookup_nvlist_array(child[c],
+ ZPOOL_CONFIG_CHILDREN, &nvchild, &nchildren) != 0) {
+ be_print_err(gettext("be_do_installgrub: "
+ "failed to traverse the vdev tree: %s\n"),
+ libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ goto done;
+ }
+
+ for (i = 0; i < nchildren; i++) {
+ vname = zpool_vdev_name(g_zfs, zphp,
+ nvchild[i], B_FALSE);
+ if (vname == NULL) {
+ be_print_err(gettext(
+ "be_do_installgrub: "
+ "failed to get device name: %s\n"),
+ libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ goto done;
+ }
+
+ (void) snprintf(installgrub_cmd,
+ sizeof (installgrub_cmd),
+ "%s %s %s /dev/rdsk/%s",
+ BE_INSTALL_GRUB, stage1, stage2, vname);
+ if (be_run_cmd(installgrub_cmd,
+ be_run_cmd_errbuf, BUFSIZ, NULL, 0) !=
+ BE_SUCCESS) {
+ be_print_err(gettext(
+ "be_do_installgrub: installgrub "
+ "failed for device %s.\n"), vname);
+ /* Assume localized cmd err output. */
+ be_print_err(gettext(
+ " Command: \"%s\"\n"),
+ installgrub_cmd);
+ be_print_err("%s", be_run_cmd_errbuf);
+ free(vname);
+ ret = BE_ERR_BOOTFILE_INST;
+ goto done;
+ }
+ free(vname);
+ }
+ } else {
+ (void) snprintf(installgrub_cmd,
+ sizeof (installgrub_cmd), "%s %s %s /dev/rdsk/%s",
+ BE_INSTALL_GRUB, stage1, stage2, vname);
+ if (be_run_cmd(installgrub_cmd, be_run_cmd_errbuf,
+ BUFSIZ, NULL, 0) != BE_SUCCESS) {
+ be_print_err(gettext(
+ "be_do_installgrub: installgrub "
+ "failed for device %s.\n"), vname);
+ /* Assume localized cmd err output. */
+ be_print_err(gettext(" Command: \"%s\"\n"),
+ installgrub_cmd);
+ be_print_err("%s", be_run_cmd_errbuf);
+ free(vname);
+ ret = BE_ERR_BOOTFILE_INST;
+ goto done;
+ }
+ free(vname);
+ }
+ }
+
+ /*
+ * Copy the grub capability file from the BE we're activating into
+ * the root pool.
+ */
+ (void) snprintf(cap_file, sizeof (cap_file), "%s%s", tmp_mntpt,
+ BE_CAP_FILE);
+
+ if ((zhp = zfs_open(g_zfs, bt->obe_zpool, ZFS_TYPE_FILESYSTEM)) ==
+ NULL) {
+ be_print_err(gettext("be_do_installgrub: zfs_open "
+ "failed: %s\n"), libzfs_error_description(g_zfs));
+ zpool_close(zphp);
+ return (zfs_err_to_be_err(g_zfs));
+ }
+
+ /*
+ * Check to see if the pool's dataset is mounted. If it isn't we'll
+ * attempt to mount it.
+ */
+ if ((ret = be_mount_pool(zhp, &ptmp_mntpnt,
+ &orig_mntpnt, &pool_mounted)) != BE_SUCCESS) {
+ be_print_err(gettext("be_do_installgrub: pool dataset "
+ "(%s) could not be mounted\n"), bt->obe_zpool);
+ ZFS_CLOSE(zhp);
+ zpool_close(zphp);
+ return (ret);
+ }
+
+ /*
+ * Get the mountpoint for the root pool dataset.
+ */
+ if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
+ be_print_err(gettext("be_do_installgrub: pool "
+ "dataset (%s) is not mounted. Can't check the grub "
+ "version from the grub capability file.\n"), bt->obe_zpool);
+ ret = BE_ERR_NO_MENU;
+ goto done;
+ }
+
+ (void) snprintf(zpool_cap_file, sizeof (zpool_cap_file), "%s%s",
+ pool_mntpnt, BE_CAP_FILE);
+
+ free(pool_mntpnt);
+ pool_mntpnt = NULL;
+
+ if ((cap_fp = fopen(cap_file, "r")) == NULL) {
+ err = errno;
+ be_print_err(gettext("be_do_installgrub: failed to open grub "
+ "capability file\n"));
+ ret = errno_to_be_err(err);
+ goto done;
+ }
+ if ((zpool_cap_fp = fopen(zpool_cap_file, "w")) == NULL) {
+ err = errno;
+ be_print_err(gettext("be_do_installgrub: failed to open new "
+ "grub capability file\n"));
+ ret = errno_to_be_err(err);
+ (void) fclose(cap_fp);
+ goto done;
+ }
+
+ while (fgets(line, BUFSIZ, cap_fp)) {
+ (void) fputs(line, zpool_cap_fp);
+ }
+
+ (void) fclose(zpool_cap_fp);
+ (void) fclose(cap_fp);
+
+done:
+ if (pool_mounted) {
+ int iret = 0;
+ iret = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
+ if (ret == BE_SUCCESS)
+ ret = iret;
+ free(orig_mntpnt);
+ free(ptmp_mntpnt);
+ }
+ ZFS_CLOSE(zhp);
+ if (be_mounted)
+ (void) _be_unmount(bt->obe_name, 0);
+ zpool_close(zphp);
+ free(tmp_mntpt);
+ return (ret);
+}
+
+/*
+ * Function: be_promote_zone_ds
+ * Description: This function finds the zones for the BE being activated
+ * and the active zonepath dataset for each zone. Then each
+ * active zonepath dataset is promoted.
+ *
+ * Parameters:
+ * be_name - the name of the global zone BE that we need to
+ * find the zones for.
+ * be_root_ds - the root dataset for be_name.
+ * Return:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ *
+ * Scope:
+ * Private
+ */
+static int
+be_promote_zone_ds(char *be_name, char *be_root_ds)
+{
+ char *zone_ds = NULL;
+ char *temp_mntpt = NULL;
+ char origin[MAXPATHLEN];
+ char zoneroot_ds[MAXPATHLEN];
+ zfs_handle_t *zhp = NULL;
+ zfs_handle_t *z_zhp = NULL;
+ zoneList_t zone_list = NULL;
+ zoneBrandList_t *brands = NULL;
+ boolean_t be_mounted = B_FALSE;
+ int zone_index = 0;
+ int err = BE_SUCCESS;
+
+ /*
+ * Get the supported zone brands so we can pass that
+ * to z_get_nonglobal_zone_list_by_brand. Currently
+ * only the ipkg and labeled brand zones are supported
+ *
+ */
+ if ((brands = be_get_supported_brandlist()) == NULL) {
+ be_print_err(gettext("be_promote_zone_ds: no supported "
+ "brands\n"));
+ return (BE_SUCCESS);
+ }
+
+ if ((zhp = zfs_open(g_zfs, be_root_ds,
+ ZFS_TYPE_FILESYSTEM)) == NULL) {
+ be_print_err(gettext("be_promote_zone_ds: Failed to open "
+ "dataset (%s): %s\n"), be_root_ds,
+ libzfs_error_description(g_zfs));
+ err = zfs_err_to_be_err(g_zfs);
+ z_free_brand_list(brands);
+ return (err);
+ }
+
+ if (!zfs_is_mounted(zhp, &temp_mntpt)) {
+ if ((err = _be_mount(be_name, &temp_mntpt,
+ BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) {
+ be_print_err(gettext("be_promote_zone_ds: failed to "
+ "mount the BE for zones procesing.\n"));
+ ZFS_CLOSE(zhp);
+ z_free_brand_list(brands);
+ return (err);
+ }
+ be_mounted = B_TRUE;
+ }
+
+ /*
+ * Set the zone root to the temp mount point for the BE we just mounted.
+ */
+ z_set_zone_root(temp_mntpt);
+
+ /*
+ * Get all the zones based on the brands we're looking for. If no zones
+ * are found that we're interested in unmount the BE and move on.
+ */
+ if ((zone_list = z_get_nonglobal_zone_list_by_brand(brands)) == NULL) {
+ if (be_mounted)
+ (void) _be_unmount(be_name, 0);
+ ZFS_CLOSE(zhp);
+ z_free_brand_list(brands);
+ free(temp_mntpt);
+ return (BE_SUCCESS);
+ }
+ for (zone_index = 0; z_zlist_get_zonename(zone_list, zone_index)
+ != NULL; zone_index++) {
+ char *zone_path = NULL;
+
+ /* Skip zones that aren't at least installed */
+ if (z_zlist_get_current_state(zone_list, zone_index) <
+ ZONE_STATE_INSTALLED)
+ continue;
+
+ if (((zone_path =
+ z_zlist_get_zonepath(zone_list, zone_index)) == NULL) ||
+ ((zone_ds = be_get_ds_from_dir(zone_path)) == NULL) ||
+ !be_zone_supported(zone_ds))
+ continue;
+
+ if (be_find_active_zone_root(zhp, zone_ds,
+ zoneroot_ds, sizeof (zoneroot_ds)) != 0) {
+ be_print_err(gettext("be_promote_zone_ds: "
+ "Zone does not have an active root "
+ "dataset, skipping this zone.\n"));
+ continue;
+ }
+
+ if ((z_zhp = zfs_open(g_zfs, zoneroot_ds,
+ ZFS_TYPE_FILESYSTEM)) == NULL) {
+ be_print_err(gettext("be_promote_zone_ds: "
+ "Failed to open dataset "
+ "(%s): %s\n"), zoneroot_ds,
+ libzfs_error_description(g_zfs));
+ err = zfs_err_to_be_err(g_zfs);
+ goto done;
+ }
+
+ if (zfs_prop_get(z_zhp, ZFS_PROP_ORIGIN, origin,
+ sizeof (origin), NULL, NULL, 0, B_FALSE) != 0) {
+ ZFS_CLOSE(z_zhp);
+ continue;
+ }
+
+ /*
+ * We don't need to close the zfs handle at this
+ * point because the callback funtion
+ * be_promote_ds_callback() will close it for us.
+ */
+ if (be_promote_ds_callback(z_zhp, NULL) != 0) {
+ be_print_err(gettext("be_promote_zone_ds: "
+ "failed to activate the "
+ "datasets for %s: %s\n"),
+ zoneroot_ds,
+ libzfs_error_description(g_zfs));
+ err = BE_ERR_PROMOTE;
+ goto done;
+ }
+ }
+done:
+ if (be_mounted)
+ (void) _be_unmount(be_name, 0);
+ ZFS_CLOSE(zhp);
+ free(temp_mntpt);
+ z_free_brand_list(brands);
+ z_free_zone_list(zone_list);
+ return (err);
+}
+
+/*
+ * Function: be_promote_ds_callback
+ * Description: This function is used to promote the datasets for the BE
+ * being activated as well as the datasets for the zones BE
+ * being activated.
+ *
+ * Parameters:
+ * zhp - the zfs handle for zone BE being activated.
+ * data - not used.
+ * Return:
+ * 0 - Success
+ * be_errno_t - Failure
+ *
+ * Scope:
+ * Private
+ */
+static int
+/* LINTED */
+be_promote_ds_callback(zfs_handle_t *zhp, void *data)
+{
+ char origin[MAXPATHLEN];
+ char *sub_dataset = NULL;
+ int ret = 0;
+
+ if (zhp != NULL) {
+ sub_dataset = strdup(zfs_get_name(zhp));
+ if (sub_dataset == NULL) {
+ ret = BE_ERR_NOMEM;
+ goto done;
+ }
+ } else {
+ be_print_err(gettext("be_promote_ds_callback: "
+ "Invalid zfs handle passed into function\n"));
+ ret = BE_ERR_INVAL;
+ goto done;
+ }
+
+ /*
+ * This loop makes sure that we promote the dataset to the
+ * top of the tree so that it is no longer a decendent of any
+ * dataset. The ZFS close and then open is used to make sure that
+ * the promotion is updated before we move on.
+ */
+ while (zfs_prop_get(zhp, ZFS_PROP_ORIGIN, origin,
+ sizeof (origin), NULL, NULL, 0, B_FALSE) == 0) {
+ if (zfs_promote(zhp) != 0) {
+ if (libzfs_errno(g_zfs) != EZFS_EXISTS) {
+ be_print_err(gettext("be_promote_ds_callback: "
+ "promote of %s failed: %s\n"),
+ zfs_get_name(zhp),
+ libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ goto done;
+ } else {
+ /*
+ * If the call to zfs_promote returns the
+ * error EZFS_EXISTS we've hit a snapshot name
+ * collision. This means we're probably
+ * attemping to promote a zone dataset above a
+ * parent dataset that belongs to another zone
+ * which this zone was cloned from.
+ *
+ * TODO: If this is a zone dataset at some
+ * point we should skip this if the zone
+ * paths for the dataset and the snapshot
+ * don't match.
+ */
+ be_print_err(gettext("be_promote_ds_callback: "
+ "promote of %s failed due to snapshot "
+ "name collision: %s\n"), zfs_get_name(zhp),
+ libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ goto done;
+ }
+ }
+ ZFS_CLOSE(zhp);
+ if ((zhp = zfs_open(g_zfs, sub_dataset,
+ ZFS_TYPE_FILESYSTEM)) == NULL) {
+ be_print_err(gettext("be_promote_ds_callback: "
+ "Failed to open dataset (%s): %s\n"), sub_dataset,
+ libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ goto done;
+ }
+ }
+
+ /* Iterate down this dataset's children and promote them */
+ ret = zfs_iter_filesystems(zhp, be_promote_ds_callback, NULL);
+
+done:
+ free(sub_dataset);
+ ZFS_CLOSE(zhp);
+ return (ret);
+}