/* * 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_get_node_data * Description: Helper function used to collect all the information to fill * in the be_node_list structure to be returned by be_list. * Parameters: * zhp - Handle to the root dataset for the BE whose information * we're collecting. * be_node - a pointer to the node structure we're filling in. * be_name - The BE name of the node whose information we're * collecting. * current_be - the name of the currently active BE. * be_ds - The dataset name for the BE. * * Returns: * BE_SUCCESS - Success * be_errno_t - Failure * Scope: * Private */ static int be_get_node_data( zfs_handle_t *zhp, be_node_list_t *be_node, char *be_name, const char *rpool, char *current_be, char *be_ds) { char prop_buf[MAXPATHLEN]; nvlist_t *userprops = NULL; nvlist_t *propval = NULL; nvlist_t *zone_propval = NULL; char *prop_str = NULL; char *zone_prop_str = NULL; char *grub_default_bootfs = NULL; zpool_handle_t *zphp = NULL; int err = 0; if (be_node == NULL || be_name == NULL || current_be == NULL || be_ds == NULL) { be_print_err(gettext("be_get_node_data: invalid arguments, " "can not be NULL\n")); return (BE_ERR_INVAL); } errno = 0; be_node->be_root_ds = strdup(be_ds); if ((err = errno) != 0 || be_node->be_root_ds == NULL) { be_print_err(gettext("be_get_node_data: failed to " "copy root dataset name\n")); return (errno_to_be_err(err)); } be_node->be_node_name = strdup(be_name); if ((err = errno) != 0 || be_node->be_node_name == NULL) { be_print_err(gettext("be_get_node_data: failed to " "copy BE name\n")); return (errno_to_be_err(err)); } if (strncmp(be_name, current_be, MAXPATHLEN) == 0) be_node->be_active = B_TRUE; else be_node->be_active = B_FALSE; be_node->be_rpool = strdup(rpool); if (be_node->be_rpool == NULL || (err = errno) != 0) { be_print_err(gettext("be_get_node_data: failed to " "copy root pool name\n")); return (errno_to_be_err(err)); } be_node->be_space_used = zfs_prop_get_int(zhp, ZFS_PROP_USED); if (getzoneid() == GLOBAL_ZONEID) { if ((zphp = zpool_open(g_zfs, rpool)) == NULL) { be_print_err(gettext("be_get_node_data: failed to open " "pool (%s): %s\n"), rpool, libzfs_error_description(g_zfs)); return (zfs_err_to_be_err(g_zfs)); } (void) zpool_get_prop(zphp, ZPOOL_PROP_BOOTFS, prop_buf, ZFS_MAXPROPLEN, NULL, B_FALSE); if (be_has_grub() && (be_default_grub_bootfs(rpool, &grub_default_bootfs) == BE_SUCCESS) && grub_default_bootfs != NULL) if (strcmp(grub_default_bootfs, be_ds) == 0) be_node->be_active_on_boot = B_TRUE; else be_node->be_active_on_boot = B_FALSE; else if (prop_buf != NULL && strcmp(prop_buf, be_ds) == 0) be_node->be_active_on_boot = B_TRUE; else be_node->be_active_on_boot = B_FALSE; be_node->be_global_active = B_TRUE; free(grub_default_bootfs); zpool_close(zphp); } else { if (be_zone_compare_uuids(be_node->be_root_ds)) be_node->be_global_active = B_TRUE; else be_node->be_global_active = B_FALSE; } /* * If the dataset is mounted use the mount point * returned from the zfs_is_mounted call. If the * dataset is not mounted then pull the mount * point information out of the zfs properties. */ be_node->be_mounted = zfs_is_mounted(zhp, &(be_node->be_mntpt)); if (!be_node->be_mounted) { if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, prop_buf, ZFS_MAXPROPLEN, NULL, NULL, 0, B_FALSE) == 0) be_node->be_mntpt = strdup(prop_buf); else return (zfs_err_to_be_err(g_zfs)); } be_node->be_node_creation = (time_t)zfs_prop_get_int(zhp, ZFS_PROP_CREATION); /* Get all user properties used for libbe */ if ((userprops = zfs_get_user_props(zhp)) == NULL) { be_node->be_policy_type = strdup(be_default_policy()); } else { if (getzoneid() != GLOBAL_ZONEID) { if (nvlist_lookup_nvlist(userprops, BE_ZONE_ACTIVE_PROPERTY, &zone_propval) != 0 || zone_propval == NULL) { be_node->be_active_on_boot = B_FALSE; } else { verify(nvlist_lookup_string(zone_propval, ZPROP_VALUE, &zone_prop_str) == 0); if (strcmp(zone_prop_str, "on") == 0) { be_node->be_active_on_boot = B_TRUE; } else { be_node->be_active_on_boot = B_FALSE; } } } if (nvlist_lookup_nvlist(userprops, BE_POLICY_PROPERTY, &propval) != 0 || propval == NULL) { be_node->be_policy_type = strdup(be_default_policy()); } else { verify(nvlist_lookup_string(propval, ZPROP_VALUE, &prop_str) == 0); if (prop_str == NULL || strcmp(prop_str, "-") == 0 || strcmp(prop_str, "") == 0) be_node->be_policy_type = strdup(be_default_policy()); else be_node->be_policy_type = strdup(prop_str); } if (getzoneid() != GLOBAL_ZONEID) { if (nvlist_lookup_nvlist(userprops, BE_ZONE_PARENTBE_PROPERTY, &propval) != 0 && nvlist_lookup_string(propval, ZPROP_VALUE, &prop_str) == 0) { be_node->be_uuid_str = strdup(prop_str); } } else { if (nvlist_lookup_nvlist(userprops, BE_UUID_PROPERTY, &propval) == 0 && nvlist_lookup_string(propval, ZPROP_VALUE, &prop_str) == 0) { be_node->be_uuid_str = strdup(prop_str); } } } /* * Increment the dataset counter to include the root dataset * of the BE. */ be_node->be_node_num_datasets++; 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_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: _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); }