summaryrefslogtreecommitdiffstats
path: root/contrib/opensolaris/cmd/zpool/zpool_main.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/opensolaris/cmd/zpool/zpool_main.c')
-rw-r--r--contrib/opensolaris/cmd/zpool/zpool_main.c3567
1 files changed, 3567 insertions, 0 deletions
diff --git a/contrib/opensolaris/cmd/zpool/zpool_main.c b/contrib/opensolaris/cmd/zpool/zpool_main.c
new file mode 100644
index 0000000..e8dc64a
--- /dev/null
+++ b/contrib/opensolaris/cmd/zpool/zpool_main.c
@@ -0,0 +1,3567 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <solaris.h>
+#include <assert.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <libintl.h>
+#include <libuutil.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+#include <priv.h>
+#include <sys/time.h>
+#include <sys/fs/zfs.h>
+
+#include <sys/stat.h>
+
+#include <libzfs.h>
+
+#include "zpool_util.h"
+
+static int zpool_do_create(int, char **);
+static int zpool_do_destroy(int, char **);
+
+static int zpool_do_add(int, char **);
+static int zpool_do_remove(int, char **);
+
+static int zpool_do_list(int, char **);
+static int zpool_do_iostat(int, char **);
+static int zpool_do_status(int, char **);
+
+static int zpool_do_online(int, char **);
+static int zpool_do_offline(int, char **);
+static int zpool_do_clear(int, char **);
+
+static int zpool_do_attach(int, char **);
+static int zpool_do_detach(int, char **);
+static int zpool_do_replace(int, char **);
+
+static int zpool_do_scrub(int, char **);
+
+static int zpool_do_import(int, char **);
+static int zpool_do_export(int, char **);
+
+static int zpool_do_upgrade(int, char **);
+
+static int zpool_do_history(int, char **);
+
+static int zpool_do_get(int, char **);
+static int zpool_do_set(int, char **);
+
+/*
+ * These libumem hooks provide a reasonable set of defaults for the allocator's
+ * debugging facilities.
+ */
+const char *
+_umem_debug_init(void)
+{
+ return ("default,verbose"); /* $UMEM_DEBUG setting */
+}
+
+const char *
+_umem_logging_init(void)
+{
+ return ("fail,contents"); /* $UMEM_LOGGING setting */
+}
+
+typedef enum {
+ HELP_ADD,
+ HELP_ATTACH,
+ HELP_CLEAR,
+ HELP_CREATE,
+ HELP_DESTROY,
+ HELP_DETACH,
+ HELP_EXPORT,
+ HELP_HISTORY,
+ HELP_IMPORT,
+ HELP_IOSTAT,
+ HELP_LIST,
+ HELP_OFFLINE,
+ HELP_ONLINE,
+ HELP_REPLACE,
+ HELP_REMOVE,
+ HELP_SCRUB,
+ HELP_STATUS,
+ HELP_UPGRADE,
+ HELP_GET,
+ HELP_SET
+} zpool_help_t;
+
+
+typedef struct zpool_command {
+ const char *name;
+ int (*func)(int, char **);
+ zpool_help_t usage;
+} zpool_command_t;
+
+/*
+ * Master command table. Each ZFS command has a name, associated function, and
+ * usage message. The usage messages need to be internationalized, so we have
+ * to have a function to return the usage message based on a command index.
+ *
+ * These commands are organized according to how they are displayed in the usage
+ * message. An empty command (one with a NULL name) indicates an empty line in
+ * the generic usage message.
+ */
+static zpool_command_t command_table[] = {
+ { "create", zpool_do_create, HELP_CREATE },
+ { "destroy", zpool_do_destroy, HELP_DESTROY },
+ { NULL },
+ { "add", zpool_do_add, HELP_ADD },
+ { "remove", zpool_do_remove, HELP_REMOVE },
+ { NULL },
+ { "list", zpool_do_list, HELP_LIST },
+ { "iostat", zpool_do_iostat, HELP_IOSTAT },
+ { "status", zpool_do_status, HELP_STATUS },
+ { NULL },
+ { "online", zpool_do_online, HELP_ONLINE },
+ { "offline", zpool_do_offline, HELP_OFFLINE },
+ { "clear", zpool_do_clear, HELP_CLEAR },
+ { NULL },
+ { "attach", zpool_do_attach, HELP_ATTACH },
+ { "detach", zpool_do_detach, HELP_DETACH },
+ { "replace", zpool_do_replace, HELP_REPLACE },
+ { NULL },
+ { "scrub", zpool_do_scrub, HELP_SCRUB },
+ { NULL },
+ { "import", zpool_do_import, HELP_IMPORT },
+ { "export", zpool_do_export, HELP_EXPORT },
+ { "upgrade", zpool_do_upgrade, HELP_UPGRADE },
+ { NULL },
+ { "history", zpool_do_history, HELP_HISTORY },
+ { "get", zpool_do_get, HELP_GET },
+ { "set", zpool_do_set, HELP_SET },
+};
+
+#define NCOMMAND (sizeof (command_table) / sizeof (command_table[0]))
+
+zpool_command_t *current_command;
+
+static const char *
+get_usage(zpool_help_t idx) {
+ switch (idx) {
+ case HELP_ADD:
+ return (gettext("\tadd [-fn] <pool> <vdev> ...\n"));
+ case HELP_ATTACH:
+ return (gettext("\tattach [-f] <pool> <device> "
+ "<new_device>\n"));
+ case HELP_CLEAR:
+ return (gettext("\tclear <pool> [device]\n"));
+ case HELP_CREATE:
+ return (gettext("\tcreate [-fn] [-R root] [-m mountpoint] "
+ "<pool> <vdev> ...\n"));
+ case HELP_DESTROY:
+ return (gettext("\tdestroy [-f] <pool>\n"));
+ case HELP_DETACH:
+ return (gettext("\tdetach <pool> <device>\n"));
+ case HELP_EXPORT:
+ return (gettext("\texport [-f] <pool> ...\n"));
+ case HELP_HISTORY:
+ return (gettext("\thistory [<pool>]\n"));
+ case HELP_IMPORT:
+ return (gettext("\timport [-d dir] [-D]\n"
+ "\timport [-d dir] [-D] [-f] [-o opts] [-R root] -a\n"
+ "\timport [-d dir] [-D] [-f] [-o opts] [-R root ]"
+ " <pool | id> [newpool]\n"));
+ case HELP_IOSTAT:
+ return (gettext("\tiostat [-v] [pool] ... [interval "
+ "[count]]\n"));
+ case HELP_LIST:
+ return (gettext("\tlist [-H] [-o field[,field]*] "
+ "[pool] ...\n"));
+ case HELP_OFFLINE:
+ return (gettext("\toffline [-t] <pool> <device> ...\n"));
+ case HELP_ONLINE:
+ return (gettext("\tonline <pool> <device> ...\n"));
+ case HELP_REPLACE:
+ return (gettext("\treplace [-f] <pool> <device> "
+ "[new_device]\n"));
+ case HELP_REMOVE:
+ return (gettext("\tremove <pool> <device>\n"));
+ case HELP_SCRUB:
+ return (gettext("\tscrub [-s] <pool> ...\n"));
+ case HELP_STATUS:
+ return (gettext("\tstatus [-vx] [pool] ...\n"));
+ case HELP_UPGRADE:
+ return (gettext("\tupgrade\n"
+ "\tupgrade -v\n"
+ "\tupgrade <-a | pool>\n"));
+ case HELP_GET:
+ return (gettext("\tget <all | property[,property]...> "
+ "<pool> ...\n"));
+ case HELP_SET:
+ return (gettext("\tset <property=value> <pool> \n"));
+ }
+
+ abort();
+ /* NOTREACHED */
+}
+
+/*
+ * Fields available for 'zpool list'.
+ */
+typedef enum {
+ ZPOOL_FIELD_NAME,
+ ZPOOL_FIELD_SIZE,
+ ZPOOL_FIELD_USED,
+ ZPOOL_FIELD_AVAILABLE,
+ ZPOOL_FIELD_CAPACITY,
+ ZPOOL_FIELD_HEALTH,
+ ZPOOL_FIELD_ROOT
+} zpool_field_t;
+
+#define MAX_FIELDS 10
+
+typedef struct column_def {
+ const char *cd_title;
+ size_t cd_width;
+ enum {
+ left_justify,
+ right_justify
+ } cd_justify;
+} column_def_t;
+
+static column_def_t column_table[] = {
+ { "NAME", 20, left_justify },
+ { "SIZE", 6, right_justify },
+ { "USED", 6, right_justify },
+ { "AVAIL", 6, right_justify },
+ { "CAP", 5, right_justify },
+ { "HEALTH", 9, left_justify },
+ { "ALTROOT", 15, left_justify }
+};
+
+static char *column_subopts[] = {
+ "name",
+ "size",
+ "used",
+ "available",
+ "capacity",
+ "health",
+ "root",
+ NULL
+};
+
+/*
+ * Callback routine that will print out a pool property value.
+ */
+static zpool_prop_t
+print_prop_cb(zpool_prop_t prop, void *cb)
+{
+ FILE *fp = cb;
+
+ (void) fprintf(fp, "\t%-13s ", zpool_prop_to_name(prop));
+
+ if (zpool_prop_values(prop) == NULL)
+ (void) fprintf(fp, "-\n");
+ else
+ (void) fprintf(fp, "%s\n", zpool_prop_values(prop));
+
+ return (ZFS_PROP_CONT);
+}
+
+/*
+ * Display usage message. If we're inside a command, display only the usage for
+ * that command. Otherwise, iterate over the entire command table and display
+ * a complete usage message.
+ */
+void
+usage(boolean_t requested)
+{
+ int i;
+ FILE *fp = requested ? stdout : stderr;
+
+ if (current_command == NULL) {
+ int i;
+
+ (void) fprintf(fp, gettext("usage: zpool command args ...\n"));
+ (void) fprintf(fp,
+ gettext("where 'command' is one of the following:\n\n"));
+
+ for (i = 0; i < NCOMMAND; i++) {
+ if (command_table[i].name == NULL)
+ (void) fprintf(fp, "\n");
+ else
+ (void) fprintf(fp, "%s",
+ get_usage(command_table[i].usage));
+ }
+ } else {
+ (void) fprintf(fp, gettext("usage:\n"));
+ (void) fprintf(fp, "%s", get_usage(current_command->usage));
+
+ if (strcmp(current_command->name, "list") == 0) {
+ (void) fprintf(fp, gettext("\nwhere 'field' is one "
+ "of the following:\n\n"));
+
+ for (i = 0; column_subopts[i] != NULL; i++)
+ (void) fprintf(fp, "\t%s\n", column_subopts[i]);
+ }
+ }
+
+ if (current_command != NULL &&
+ ((strcmp(current_command->name, "set") == 0) ||
+ (strcmp(current_command->name, "get") == 0))) {
+
+ (void) fprintf(fp,
+ gettext("\nthe following properties are supported:\n"));
+
+ (void) fprintf(fp, "\n\t%-13s %s\n\n",
+ "PROPERTY", "VALUES");
+
+ /* Iterate over all properties */
+ (void) zpool_prop_iter(print_prop_cb, fp, B_FALSE);
+ }
+
+ /*
+ * See comments at end of main().
+ */
+ if (getenv("ZFS_ABORT") != NULL) {
+ (void) printf("dumping core by request\n");
+ abort();
+ }
+
+ exit(requested ? 0 : 2);
+}
+
+const char *
+state_to_health(int vs_state)
+{
+ switch (vs_state) {
+ case VDEV_STATE_CLOSED:
+ case VDEV_STATE_CANT_OPEN:
+ case VDEV_STATE_OFFLINE:
+ return (dgettext(TEXT_DOMAIN, "FAULTED"));
+ case VDEV_STATE_DEGRADED:
+ return (dgettext(TEXT_DOMAIN, "DEGRADED"));
+ case VDEV_STATE_HEALTHY:
+ return (dgettext(TEXT_DOMAIN, "ONLINE"));
+ }
+
+ return (dgettext(TEXT_DOMAIN, "UNKNOWN"));
+}
+
+const char *
+state_to_name(vdev_stat_t *vs)
+{
+ switch (vs->vs_state) {
+ case VDEV_STATE_CLOSED:
+ case VDEV_STATE_CANT_OPEN:
+ if (vs->vs_aux == VDEV_AUX_CORRUPT_DATA)
+ return (gettext("FAULTED"));
+ else
+ return (gettext("UNAVAIL"));
+ case VDEV_STATE_OFFLINE:
+ return (gettext("OFFLINE"));
+ case VDEV_STATE_DEGRADED:
+ return (gettext("DEGRADED"));
+ case VDEV_STATE_HEALTHY:
+ return (gettext("ONLINE"));
+ }
+
+ return (gettext("UNKNOWN"));
+}
+
+void
+print_vdev_tree(zpool_handle_t *zhp, const char *name, nvlist_t *nv, int indent)
+{
+ nvlist_t **child;
+ uint_t c, children;
+ char *vname;
+
+ if (name != NULL)
+ (void) printf("\t%*s%s\n", indent, "", name);
+
+ if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
+ &child, &children) != 0)
+ return;
+
+ for (c = 0; c < children; c++) {
+ vname = zpool_vdev_name(g_zfs, zhp, child[c]);
+ print_vdev_tree(zhp, vname, child[c], indent + 2);
+ free(vname);
+ }
+}
+
+/*
+ * zpool add [-fn] <pool> <vdev> ...
+ *
+ * -f Force addition of devices, even if they appear in use
+ * -n Do not add the devices, but display the resulting layout if
+ * they were to be added.
+ *
+ * Adds the given vdevs to 'pool'. As with create, the bulk of this work is
+ * handled by get_vdev_spec(), which constructs the nvlist needed to pass to
+ * libzfs.
+ */
+int
+zpool_do_add(int argc, char **argv)
+{
+ boolean_t force = B_FALSE;
+ boolean_t dryrun = B_FALSE;
+ int c;
+ nvlist_t *nvroot;
+ char *poolname;
+ int ret;
+ zpool_handle_t *zhp;
+ nvlist_t *config;
+
+ /* check options */
+ while ((c = getopt(argc, argv, "fn")) != -1) {
+ switch (c) {
+ case 'f':
+ force = B_TRUE;
+ break;
+ case 'n':
+ dryrun = B_TRUE;
+ break;
+ case '?':
+ (void) fprintf(stderr, gettext("invalid option '%c'\n"),
+ optopt);
+ usage(B_FALSE);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ /* get pool name and check number of arguments */
+ if (argc < 1) {
+ (void) fprintf(stderr, gettext("missing pool name argument\n"));
+ usage(B_FALSE);
+ }
+ if (argc < 2) {
+ (void) fprintf(stderr, gettext("missing vdev specification\n"));
+ usage(B_FALSE);
+ }
+
+ poolname = argv[0];
+
+ argc--;
+ argv++;
+
+ if ((zhp = zpool_open(g_zfs, poolname)) == NULL)
+ return (1);
+
+ if ((config = zpool_get_config(zhp, NULL)) == NULL) {
+ (void) fprintf(stderr, gettext("pool '%s' is unavailable\n"),
+ poolname);
+ zpool_close(zhp);
+ return (1);
+ }
+
+ /* pass off to get_vdev_spec for processing */
+ nvroot = make_root_vdev(config, force, !force, B_FALSE, argc, argv);
+ if (nvroot == NULL) {
+ zpool_close(zhp);
+ return (1);
+ }
+
+ if (dryrun) {
+ nvlist_t *poolnvroot;
+
+ verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
+ &poolnvroot) == 0);
+
+ (void) printf(gettext("would update '%s' to the following "
+ "configuration:\n"), zpool_get_name(zhp));
+
+ print_vdev_tree(zhp, poolname, poolnvroot, 0);
+ print_vdev_tree(zhp, NULL, nvroot, 0);
+
+ ret = 0;
+ } else {
+ ret = (zpool_add(zhp, nvroot) != 0);
+ if (!ret) {
+ zpool_log_history(g_zfs, argc + 1 + optind,
+ argv - 1 - optind, poolname, B_TRUE, B_FALSE);
+ }
+ }
+
+ nvlist_free(nvroot);
+ zpool_close(zhp);
+
+ return (ret);
+}
+
+/*
+ * zpool remove <pool> <vdev>
+ *
+ * Removes the given vdev from the pool. Currently, this only supports removing
+ * spares from the pool. Eventually, we'll want to support removing leaf vdevs
+ * (as an alias for 'detach') as well as toplevel vdevs.
+ */
+int
+zpool_do_remove(int argc, char **argv)
+{
+ char *poolname;
+ int ret;
+ zpool_handle_t *zhp;
+
+ argc--;
+ argv++;
+
+ /* get pool name and check number of arguments */
+ if (argc < 1) {
+ (void) fprintf(stderr, gettext("missing pool name argument\n"));
+ usage(B_FALSE);
+ }
+ if (argc < 2) {
+ (void) fprintf(stderr, gettext("missing device\n"));
+ usage(B_FALSE);
+ }
+
+ poolname = argv[0];
+
+ if ((zhp = zpool_open(g_zfs, poolname)) == NULL)
+ return (1);
+
+ ret = (zpool_vdev_remove(zhp, argv[1]) != 0);
+ if (!ret) {
+ zpool_log_history(g_zfs, ++argc, --argv, poolname, B_TRUE,
+ B_FALSE);
+ }
+
+ return (ret);
+}
+
+/*
+ * zpool create [-fn] [-R root] [-m mountpoint] <pool> <dev> ...
+ *
+ * -f Force creation, even if devices appear in use
+ * -n Do not create the pool, but display the resulting layout if it
+ * were to be created.
+ * -R Create a pool under an alternate root
+ * -m Set default mountpoint for the root dataset. By default it's
+ * '/<pool>'
+ *
+ * Creates the named pool according to the given vdev specification. The
+ * bulk of the vdev processing is done in get_vdev_spec() in zpool_vdev.c. Once
+ * we get the nvlist back from get_vdev_spec(), we either print out the contents
+ * (if '-n' was specified), or pass it to libzfs to do the creation.
+ */
+int
+zpool_do_create(int argc, char **argv)
+{
+ boolean_t force = B_FALSE;
+ boolean_t dryrun = B_FALSE;
+ int c;
+ nvlist_t *nvroot;
+ char *poolname;
+ int ret;
+ char *altroot = NULL;
+ char *mountpoint = NULL;
+ nvlist_t **child;
+ uint_t children;
+
+ /* check options */
+ while ((c = getopt(argc, argv, ":fnR:m:")) != -1) {
+ switch (c) {
+ case 'f':
+ force = B_TRUE;
+ break;
+ case 'n':
+ dryrun = B_TRUE;
+ break;
+ case 'R':
+ altroot = optarg;
+ break;
+ case 'm':
+ mountpoint = optarg;
+ break;
+ case ':':
+ (void) fprintf(stderr, gettext("missing argument for "
+ "'%c' option\n"), optopt);
+ usage(B_FALSE);
+ break;
+ case '?':
+ (void) fprintf(stderr, gettext("invalid option '%c'\n"),
+ optopt);
+ usage(B_FALSE);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ /* get pool name and check number of arguments */
+ if (argc < 1) {
+ (void) fprintf(stderr, gettext("missing pool name argument\n"));
+ usage(B_FALSE);
+ }
+ if (argc < 2) {
+ (void) fprintf(stderr, gettext("missing vdev specification\n"));
+ usage(B_FALSE);
+ }
+
+ poolname = argv[0];
+
+ /*
+ * As a special case, check for use of '/' in the name, and direct the
+ * user to use 'zfs create' instead.
+ */
+ if (strchr(poolname, '/') != NULL) {
+ (void) fprintf(stderr, gettext("cannot create '%s': invalid "
+ "character '/' in pool name\n"), poolname);
+ (void) fprintf(stderr, gettext("use 'zfs create' to "
+ "create a dataset\n"));
+ return (1);
+ }
+
+ /* pass off to get_vdev_spec for bulk processing */
+ nvroot = make_root_vdev(NULL, force, !force, B_FALSE, argc - 1,
+ argv + 1);
+ if (nvroot == NULL)
+ return (1);
+
+ /* make_root_vdev() allows 0 toplevel children if there are spares */
+ verify(nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN,
+ &child, &children) == 0);
+ if (children == 0) {
+ (void) fprintf(stderr, gettext("invalid vdev "
+ "specification: at least one toplevel vdev must be "
+ "specified\n"));
+ return (1);
+ }
+
+
+ if (altroot != NULL && altroot[0] != '/') {
+ (void) fprintf(stderr, gettext("invalid alternate root '%s': "
+ "must be an absolute path\n"), altroot);
+ nvlist_free(nvroot);
+ return (1);
+ }
+
+ /*
+ * Check the validity of the mountpoint and direct the user to use the
+ * '-m' mountpoint option if it looks like its in use.
+ */
+ if (mountpoint == NULL ||
+ (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) != 0 &&
+ strcmp(mountpoint, ZFS_MOUNTPOINT_NONE) != 0)) {
+ char buf[MAXPATHLEN];
+ struct stat64 statbuf;
+
+ if (mountpoint && mountpoint[0] != '/') {
+ (void) fprintf(stderr, gettext("invalid mountpoint "
+ "'%s': must be an absolute path, 'legacy', or "
+ "'none'\n"), mountpoint);
+ nvlist_free(nvroot);
+ return (1);
+ }
+
+ if (mountpoint == NULL) {
+ if (altroot != NULL)
+ (void) snprintf(buf, sizeof (buf), "%s/%s",
+ altroot, poolname);
+ else
+ (void) snprintf(buf, sizeof (buf), "/%s",
+ poolname);
+ } else {
+ if (altroot != NULL)
+ (void) snprintf(buf, sizeof (buf), "%s%s",
+ altroot, mountpoint);
+ else
+ (void) snprintf(buf, sizeof (buf), "%s",
+ mountpoint);
+ }
+
+ if (stat64(buf, &statbuf) == 0 &&
+ statbuf.st_nlink != 2) {
+ if (mountpoint == NULL)
+ (void) fprintf(stderr, gettext("default "
+ "mountpoint '%s' exists and is not "
+ "empty\n"), buf);
+ else
+ (void) fprintf(stderr, gettext("mountpoint "
+ "'%s' exists and is not empty\n"), buf);
+ (void) fprintf(stderr, gettext("use '-m' "
+ "option to provide a different default\n"));
+ nvlist_free(nvroot);
+ return (1);
+ }
+ }
+
+
+ if (dryrun) {
+ /*
+ * For a dry run invocation, print out a basic message and run
+ * through all the vdevs in the list and print out in an
+ * appropriate hierarchy.
+ */
+ (void) printf(gettext("would create '%s' with the "
+ "following layout:\n\n"), poolname);
+
+ print_vdev_tree(NULL, poolname, nvroot, 0);
+
+ ret = 0;
+ } else {
+ ret = 1;
+ /*
+ * Hand off to libzfs.
+ */
+ if (zpool_create(g_zfs, poolname, nvroot, altroot) == 0) {
+ zfs_handle_t *pool = zfs_open(g_zfs, poolname,
+ ZFS_TYPE_FILESYSTEM);
+ if (pool != NULL) {
+ if (mountpoint != NULL)
+ verify(zfs_prop_set(pool,
+ zfs_prop_to_name(
+ ZFS_PROP_MOUNTPOINT),
+ mountpoint) == 0);
+ if (zfs_mount(pool, NULL, 0) == 0)
+ ret = zfs_share_nfs(pool);
+ zfs_close(pool);
+ }
+ zpool_log_history(g_zfs, argc + optind, argv - optind,
+ poolname, B_TRUE, B_TRUE);
+ } else if (libzfs_errno(g_zfs) == EZFS_INVALIDNAME) {
+ (void) fprintf(stderr, gettext("pool name may have "
+ "been omitted\n"));
+ }
+ }
+
+ nvlist_free(nvroot);
+
+ return (ret);
+}
+
+/*
+ * zpool destroy <pool>
+ *
+ * -f Forcefully unmount any datasets
+ *
+ * Destroy the given pool. Automatically unmounts any datasets in the pool.
+ */
+int
+zpool_do_destroy(int argc, char **argv)
+{
+ boolean_t force = B_FALSE;
+ int c;
+ char *pool;
+ zpool_handle_t *zhp;
+ int ret;
+
+ /* check options */
+ while ((c = getopt(argc, argv, "f")) != -1) {
+ switch (c) {
+ case 'f':
+ force = B_TRUE;
+ break;
+ case '?':
+ (void) fprintf(stderr, gettext("invalid option '%c'\n"),
+ optopt);
+ usage(B_FALSE);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ /* check arguments */
+ if (argc < 1) {
+ (void) fprintf(stderr, gettext("missing pool argument\n"));
+ usage(B_FALSE);
+ }
+ if (argc > 1) {
+ (void) fprintf(stderr, gettext("too many arguments\n"));
+ usage(B_FALSE);
+ }
+
+ pool = argv[0];
+
+ if ((zhp = zpool_open_canfail(g_zfs, pool)) == NULL) {
+ /*
+ * As a special case, check for use of '/' in the name, and
+ * direct the user to use 'zfs destroy' instead.
+ */
+ if (strchr(pool, '/') != NULL)
+ (void) fprintf(stderr, gettext("use 'zfs destroy' to "
+ "destroy a dataset\n"));
+ return (1);
+ }
+
+ if (zpool_disable_datasets(zhp, force) != 0) {
+ (void) fprintf(stderr, gettext("could not destroy '%s': "
+ "could not unmount datasets\n"), zpool_get_name(zhp));
+ return (1);
+ }
+
+ zpool_log_history(g_zfs, argc + optind, argv - optind, pool, B_TRUE,
+ B_FALSE);
+
+ ret = (zpool_destroy(zhp) != 0);
+
+ zpool_close(zhp);
+
+ return (ret);
+}
+
+/*
+ * zpool export [-f] <pool> ...
+ *
+ * -f Forcefully unmount datasets
+ *
+ * Export the given pools. By default, the command will attempt to cleanly
+ * unmount any active datasets within the pool. If the '-f' flag is specified,
+ * then the datasets will be forcefully unmounted.
+ */
+int
+zpool_do_export(int argc, char **argv)
+{
+ boolean_t force = B_FALSE;
+ int c;
+ zpool_handle_t *zhp;
+ int ret;
+ int i;
+
+ /* check options */
+ while ((c = getopt(argc, argv, "f")) != -1) {
+ switch (c) {
+ case 'f':
+ force = B_TRUE;
+ break;
+ case '?':
+ (void) fprintf(stderr, gettext("invalid option '%c'\n"),
+ optopt);
+ usage(B_FALSE);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ /* check arguments */
+ if (argc < 1) {
+ (void) fprintf(stderr, gettext("missing pool argument\n"));
+ usage(B_FALSE);
+ }
+
+ ret = 0;
+ for (i = 0; i < argc; i++) {
+ if ((zhp = zpool_open_canfail(g_zfs, argv[i])) == NULL) {
+ ret = 1;
+ continue;
+ }
+
+ if (zpool_disable_datasets(zhp, force) != 0) {
+ ret = 1;
+ zpool_close(zhp);
+ continue;
+ }
+
+ zpool_log_history(g_zfs, argc + optind, argv - optind, argv[i],
+ B_TRUE, B_FALSE);
+
+ if (zpool_export(zhp) != 0)
+ ret = 1;
+
+ zpool_close(zhp);
+ }
+
+ return (ret);
+}
+
+/*
+ * Given a vdev configuration, determine the maximum width needed for the device
+ * name column.
+ */
+static int
+max_width(zpool_handle_t *zhp, nvlist_t *nv, int depth, int max)
+{
+ char *name = zpool_vdev_name(g_zfs, zhp, nv);
+ nvlist_t **child;
+ uint_t c, children;
+ int ret;
+
+ if (strlen(name) + depth > max)
+ max = strlen(name) + depth;
+
+ free(name);
+
+ if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_SPARES,
+ &child, &children) == 0) {
+ for (c = 0; c < children; c++)
+ if ((ret = max_width(zhp, child[c], depth + 2,
+ max)) > max)
+ max = ret;
+ }
+
+ if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
+ &child, &children) == 0) {
+ for (c = 0; c < children; c++)
+ if ((ret = max_width(zhp, child[c], depth + 2,
+ max)) > max)
+ max = ret;
+ }
+
+
+ return (max);
+}
+
+
+/*
+ * Print the configuration of an exported pool. Iterate over all vdevs in the
+ * pool, printing out the name and status for each one.
+ */
+void
+print_import_config(const char *name, nvlist_t *nv, int namewidth, int depth)
+{
+ nvlist_t **child;
+ uint_t c, children;
+ vdev_stat_t *vs;
+ char *type, *vname;
+
+ verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE, &type) == 0);
+ if (strcmp(type, VDEV_TYPE_MISSING) == 0)
+ return;
+
+ verify(nvlist_lookup_uint64_array(nv, ZPOOL_CONFIG_STATS,
+ (uint64_t **)&vs, &c) == 0);
+
+ (void) printf("\t%*s%-*s", depth, "", namewidth - depth, name);
+
+ if (vs->vs_aux != 0) {
+ (void) printf(" %-8s ", state_to_name(vs));
+
+ switch (vs->vs_aux) {
+ case VDEV_AUX_OPEN_FAILED:
+ (void) printf(gettext("cannot open"));
+ break;
+
+ case VDEV_AUX_BAD_GUID_SUM:
+ (void) printf(gettext("missing device"));
+ break;
+
+ case VDEV_AUX_NO_REPLICAS:
+ (void) printf(gettext("insufficient replicas"));
+ break;
+
+ case VDEV_AUX_VERSION_NEWER:
+ (void) printf(gettext("newer version"));
+ break;
+
+ default:
+ (void) printf(gettext("corrupted data"));
+ break;
+ }
+ } else {
+ (void) printf(" %s", state_to_name(vs));
+ }
+ (void) printf("\n");
+
+ if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
+ &child, &children) != 0)
+ return;
+
+ for (c = 0; c < children; c++) {
+ vname = zpool_vdev_name(g_zfs, NULL, child[c]);
+ print_import_config(vname, child[c],
+ namewidth, depth + 2);
+ free(vname);
+ }
+
+ if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_SPARES,
+ &child, &children) != 0)
+ return;
+
+ (void) printf(gettext("\tspares\n"));
+ for (c = 0; c < children; c++) {
+ vname = zpool_vdev_name(g_zfs, NULL, child[c]);
+ (void) printf("\t %s\n", vname);
+ free(vname);
+ }
+}
+
+/*
+ * Display the status for the given pool.
+ */
+static void
+show_import(nvlist_t *config)
+{
+ uint64_t pool_state;
+ vdev_stat_t *vs;
+ char *name;
+ uint64_t guid;
+ char *msgid;
+ nvlist_t *nvroot;
+ int reason;
+ const char *health;
+ uint_t vsc;
+ int namewidth;
+
+ verify(nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME,
+ &name) == 0);
+ verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID,
+ &guid) == 0);
+ verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_STATE,
+ &pool_state) == 0);
+ verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
+ &nvroot) == 0);
+
+ verify(nvlist_lookup_uint64_array(nvroot, ZPOOL_CONFIG_STATS,
+ (uint64_t **)&vs, &vsc) == 0);
+ health = state_to_health(vs->vs_state);
+
+ reason = zpool_import_status(config, &msgid);
+
+ (void) printf(gettext(" pool: %s\n"), name);
+ (void) printf(gettext(" id: %llu\n"), (u_longlong_t)guid);
+ (void) printf(gettext(" state: %s"), health);
+ if (pool_state == POOL_STATE_DESTROYED)
+ (void) printf(gettext(" (DESTROYED)"));
+ (void) printf("\n");
+
+ switch (reason) {
+ case ZPOOL_STATUS_MISSING_DEV_R:
+ case ZPOOL_STATUS_MISSING_DEV_NR:
+ case ZPOOL_STATUS_BAD_GUID_SUM:
+ (void) printf(gettext("status: One or more devices are missing "
+ "from the system.\n"));
+ break;
+
+ case ZPOOL_STATUS_CORRUPT_LABEL_R:
+ case ZPOOL_STATUS_CORRUPT_LABEL_NR:
+ (void) printf(gettext("status: One or more devices contains "
+ "corrupted data.\n"));
+ break;
+
+ case ZPOOL_STATUS_CORRUPT_DATA:
+ (void) printf(gettext("status: The pool data is corrupted.\n"));
+ break;
+
+ case ZPOOL_STATUS_OFFLINE_DEV:
+ (void) printf(gettext("status: One or more devices "
+ "are offlined.\n"));
+ break;
+
+ case ZPOOL_STATUS_CORRUPT_POOL:
+ (void) printf(gettext("status: The pool metadata is "
+ "corrupted.\n"));
+ break;
+
+ case ZPOOL_STATUS_VERSION_OLDER:
+ (void) printf(gettext("status: The pool is formatted using an "
+ "older on-disk version.\n"));
+ break;
+
+ case ZPOOL_STATUS_VERSION_NEWER:
+ (void) printf(gettext("status: The pool is formatted using an "
+ "incompatible version.\n"));
+ break;
+
+ default:
+ /*
+ * No other status can be seen when importing pools.
+ */
+ assert(reason == ZPOOL_STATUS_OK);
+ }
+
+ /*
+ * Print out an action according to the overall state of the pool.
+ */
+ if (vs->vs_state == VDEV_STATE_HEALTHY) {
+ if (reason == ZPOOL_STATUS_VERSION_OLDER)
+ (void) printf(gettext("action: The pool can be "
+ "imported using its name or numeric identifier, "
+ "though\n\tsome features will not be available "
+ "without an explicit 'zpool upgrade'.\n"));
+ else
+ (void) printf(gettext("action: The pool can be "
+ "imported using its name or numeric "
+ "identifier.\n"));
+ } else if (vs->vs_state == VDEV_STATE_DEGRADED) {
+ (void) printf(gettext("action: The pool can be imported "
+ "despite missing or damaged devices. The\n\tfault "
+ "tolerance of the pool may be compromised if imported.\n"));
+ } else {
+ switch (reason) {
+ case ZPOOL_STATUS_VERSION_NEWER:
+ (void) printf(gettext("action: The pool cannot be "
+ "imported. Access the pool on a system running "
+ "newer\n\tsoftware, or recreate the pool from "
+ "backup.\n"));
+ break;
+ case ZPOOL_STATUS_MISSING_DEV_R:
+ case ZPOOL_STATUS_MISSING_DEV_NR:
+ case ZPOOL_STATUS_BAD_GUID_SUM:
+ (void) printf(gettext("action: The pool cannot be "
+ "imported. Attach the missing\n\tdevices and try "
+ "again.\n"));
+ break;
+ default:
+ (void) printf(gettext("action: The pool cannot be "
+ "imported due to damaged devices or data.\n"));
+ }
+ }
+
+ /*
+ * If the state is "closed" or "can't open", and the aux state
+ * is "corrupt data":
+ */
+ if (((vs->vs_state == VDEV_STATE_CLOSED) ||
+ (vs->vs_state == VDEV_STATE_CANT_OPEN)) &&
+ (vs->vs_aux == VDEV_AUX_CORRUPT_DATA)) {
+ if (pool_state == POOL_STATE_DESTROYED)
+ (void) printf(gettext("\tThe pool was destroyed, "
+ "but can be imported using the '-Df' flags.\n"));
+ else if (pool_state != POOL_STATE_EXPORTED)
+ (void) printf(gettext("\tThe pool may be active on "
+ "on another system, but can be imported using\n\t"
+ "the '-f' flag.\n"));
+ }
+
+ if (msgid != NULL)
+ (void) printf(gettext(" see: http://www.sun.com/msg/%s\n"),
+ msgid);
+
+ (void) printf(gettext("config:\n\n"));
+
+ namewidth = max_width(NULL, nvroot, 0, 0);
+ if (namewidth < 10)
+ namewidth = 10;
+ print_import_config(name, nvroot, namewidth, 0);
+
+ if (reason == ZPOOL_STATUS_BAD_GUID_SUM) {
+ (void) printf(gettext("\n\tAdditional devices are known to "
+ "be part of this pool, though their\n\texact "
+ "configuration cannot be determined.\n"));
+ }
+}
+
+/*
+ * Perform the import for the given configuration. This passes the heavy
+ * lifting off to zpool_import(), and then mounts the datasets contained within
+ * the pool.
+ */
+static int
+do_import(nvlist_t *config, const char *newname, const char *mntopts,
+ const char *altroot, int force, int argc, char **argv)
+{
+ zpool_handle_t *zhp;
+ char *name;
+ uint64_t state;
+ uint64_t version;
+
+ verify(nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME,
+ &name) == 0);
+
+ verify(nvlist_lookup_uint64(config,
+ ZPOOL_CONFIG_POOL_STATE, &state) == 0);
+ verify(nvlist_lookup_uint64(config,
+ ZPOOL_CONFIG_VERSION, &version) == 0);
+ if (version > ZFS_VERSION) {
+ (void) fprintf(stderr, gettext("cannot import '%s': pool "
+ "is formatted using a newer ZFS version\n"), name);
+ return (1);
+ } else if (state != POOL_STATE_EXPORTED && !force) {
+ (void) fprintf(stderr, gettext("cannot import '%s': pool "
+ "may be in use from other system\n"), name);
+ (void) fprintf(stderr, gettext("use '-f' to import anyway\n"));
+ return (1);
+ }
+
+ if (zpool_import(g_zfs, config, newname, altroot) != 0)
+ return (1);
+
+ if (newname != NULL)
+ name = (char *)newname;
+
+ zpool_log_history(g_zfs, argc, argv, name, B_TRUE, B_FALSE);
+
+ verify((zhp = zpool_open(g_zfs, name)) != NULL);
+
+ if (zpool_enable_datasets(zhp, mntopts, 0) != 0) {
+ zpool_close(zhp);
+ return (1);
+ }
+
+ zpool_close(zhp);
+ return (0);
+}
+
+/*
+ * zpool import [-d dir] [-D]
+ * import [-R root] [-D] [-d dir] [-f] -a
+ * import [-R root] [-D] [-d dir] [-f] <pool | id> [newpool]
+ *
+ * -d Scan in a specific directory, other than /dev/dsk. More than
+ * one directory can be specified using multiple '-d' options.
+ *
+ * -D Scan for previously destroyed pools or import all or only
+ * specified destroyed pools.
+ *
+ * -R Temporarily import the pool, with all mountpoints relative to
+ * the given root. The pool will remain exported when the machine
+ * is rebooted.
+ *
+ * -f Force import, even if it appears that the pool is active.
+ *
+ * -a Import all pools found.
+ *
+ * The import command scans for pools to import, and import pools based on pool
+ * name and GUID. The pool can also be renamed as part of the import process.
+ */
+int
+zpool_do_import(int argc, char **argv)
+{
+ char **searchdirs = NULL;
+ int nsearch = 0;
+ int c;
+ int err;
+ nvlist_t *pools;
+ boolean_t do_all = B_FALSE;
+ boolean_t do_destroyed = B_FALSE;
+ char *altroot = NULL;
+ char *mntopts = NULL;
+ boolean_t do_force = B_FALSE;
+ nvpair_t *elem;
+ nvlist_t *config;
+ uint64_t searchguid;
+ char *searchname;
+ nvlist_t *found_config;
+ boolean_t first;
+ uint64_t pool_state;
+
+ /* check options */
+ while ((c = getopt(argc, argv, ":Dfd:R:ao:")) != -1) {
+ switch (c) {
+ case 'a':
+ do_all = B_TRUE;
+ break;
+ case 'd':
+ if (searchdirs == NULL) {
+ searchdirs = safe_malloc(sizeof (char *));
+ } else {
+ char **tmp = safe_malloc((nsearch + 1) *
+ sizeof (char *));
+ bcopy(searchdirs, tmp, nsearch *
+ sizeof (char *));
+ free(searchdirs);
+ searchdirs = tmp;
+ }
+ searchdirs[nsearch++] = optarg;
+ break;
+ case 'D':
+ do_destroyed = B_TRUE;
+ break;
+ case 'f':
+ do_force = B_TRUE;
+ break;
+ case 'o':
+ mntopts = optarg;
+ break;
+ case 'R':
+ altroot = optarg;
+ break;
+ case ':':
+ (void) fprintf(stderr, gettext("missing argument for "
+ "'%c' option\n"), optopt);
+ usage(B_FALSE);
+ break;
+ case '?':
+ (void) fprintf(stderr, gettext("invalid option '%c'\n"),
+ optopt);
+ usage(B_FALSE);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (searchdirs == NULL) {
+ searchdirs = safe_malloc(sizeof (char *));
+ searchdirs[0] = "/dev";
+ nsearch = 1;
+ }
+
+ /* check argument count */
+ if (do_all) {
+ if (argc != 0) {
+ (void) fprintf(stderr, gettext("too many arguments\n"));
+ usage(B_FALSE);
+ }
+ } else {
+ if (argc > 2) {
+ (void) fprintf(stderr, gettext("too many arguments\n"));
+ usage(B_FALSE);
+ }
+
+ /*
+ * Check for the SYS_CONFIG privilege. We do this explicitly
+ * here because otherwise any attempt to discover pools will
+ * silently fail.
+ */
+ if (argc == 0 && !priv_ineffect(PRIV_SYS_CONFIG)) {
+ (void) fprintf(stderr, gettext("cannot "
+ "discover pools: permission denied\n"));
+ free(searchdirs);
+ return (1);
+ }
+ }
+
+ if ((pools = zpool_find_import(g_zfs, nsearch, searchdirs)) == NULL) {
+ free(searchdirs);
+ return (1);
+ }
+
+ /*
+ * We now have a list of all available pools in the given directories.
+ * Depending on the arguments given, we do one of the following:
+ *
+ * <none> Iterate through all pools and display information about
+ * each one.
+ *
+ * -a Iterate through all pools and try to import each one.
+ *
+ * <id> Find the pool that corresponds to the given GUID/pool
+ * name and import that one.
+ *
+ * -D Above options applies only to destroyed pools.
+ */
+ if (argc != 0) {
+ char *endptr;
+
+ errno = 0;
+ searchguid = strtoull(argv[0], &endptr, 10);
+ if (errno != 0 || *endptr != '\0')
+ searchname = argv[0];
+ else
+ searchname = NULL;
+ found_config = NULL;
+ }
+
+ err = 0;
+ elem = NULL;
+ first = B_TRUE;
+ while ((elem = nvlist_next_nvpair(pools, elem)) != NULL) {
+
+ verify(nvpair_value_nvlist(elem, &config) == 0);
+
+ verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_STATE,
+ &pool_state) == 0);
+ if (!do_destroyed && pool_state == POOL_STATE_DESTROYED)
+ continue;
+ if (do_destroyed && pool_state != POOL_STATE_DESTROYED)
+ continue;
+
+ if (argc == 0) {
+ if (first)
+ first = B_FALSE;
+ else if (!do_all)
+ (void) printf("\n");
+
+ if (do_all)
+ err |= do_import(config, NULL, mntopts,
+ altroot, do_force, argc + optind,
+ argv - optind);
+ else
+ show_import(config);
+ } else if (searchname != NULL) {
+ char *name;
+
+ /*
+ * We are searching for a pool based on name.
+ */
+ verify(nvlist_lookup_string(config,
+ ZPOOL_CONFIG_POOL_NAME, &name) == 0);
+
+ if (strcmp(name, searchname) == 0) {
+ if (found_config != NULL) {
+ (void) fprintf(stderr, gettext(
+ "cannot import '%s': more than "
+ "one matching pool\n"), searchname);
+ (void) fprintf(stderr, gettext(
+ "import by numeric ID instead\n"));
+ err = B_TRUE;
+ }
+ found_config = config;
+ }
+ } else {
+ uint64_t guid;
+
+ /*
+ * Search for a pool by guid.
+ */
+ verify(nvlist_lookup_uint64(config,
+ ZPOOL_CONFIG_POOL_GUID, &guid) == 0);
+
+ if (guid == searchguid)
+ found_config = config;
+ }
+ }
+
+ /*
+ * If we were searching for a specific pool, verify that we found a
+ * pool, and then do the import.
+ */
+ if (argc != 0 && err == 0) {
+ if (found_config == NULL) {
+ (void) fprintf(stderr, gettext("cannot import '%s': "
+ "no such pool available\n"), argv[0]);
+ err = B_TRUE;
+ } else {
+ err |= do_import(found_config, argc == 1 ? NULL :
+ argv[1], mntopts, altroot, do_force, argc + optind,
+ argv - optind);
+ }
+ }
+
+ /*
+ * If we were just looking for pools, report an error if none were
+ * found.
+ */
+ if (argc == 0 && first)
+ (void) fprintf(stderr,
+ gettext("no pools available to import\n"));
+
+ nvlist_free(pools);
+ free(searchdirs);
+
+ return (err ? 1 : 0);
+}
+
+typedef struct iostat_cbdata {
+ zpool_list_t *cb_list;
+ int cb_verbose;
+ int cb_iteration;
+ int cb_namewidth;
+} iostat_cbdata_t;
+
+static void
+print_iostat_separator(iostat_cbdata_t *cb)
+{
+ int i = 0;
+
+ for (i = 0; i < cb->cb_namewidth; i++)
+ (void) printf("-");
+ (void) printf(" ----- ----- ----- ----- ----- -----\n");
+}
+
+static void
+print_iostat_header(iostat_cbdata_t *cb)
+{
+ (void) printf("%*s capacity operations bandwidth\n",
+ cb->cb_namewidth, "");
+ (void) printf("%-*s used avail read write read write\n",
+ cb->cb_namewidth, "pool");
+ print_iostat_separator(cb);
+}
+
+/*
+ * Display a single statistic.
+ */
+void
+print_one_stat(uint64_t value)
+{
+ char buf[64];
+
+ zfs_nicenum(value, buf, sizeof (buf));
+ (void) printf(" %5s", buf);
+}
+
+/*
+ * Print out all the statistics for the given vdev. This can either be the
+ * toplevel configuration, or called recursively. If 'name' is NULL, then this
+ * is a verbose output, and we don't want to display the toplevel pool stats.
+ */
+void
+print_vdev_stats(zpool_handle_t *zhp, const char *name, nvlist_t *oldnv,
+ nvlist_t *newnv, iostat_cbdata_t *cb, int depth)
+{
+ nvlist_t **oldchild, **newchild;
+ uint_t c, children;
+ vdev_stat_t *oldvs, *newvs;
+ vdev_stat_t zerovs = { 0 };
+ uint64_t tdelta;
+ double scale;
+ char *vname;
+
+ if (oldnv != NULL) {
+ verify(nvlist_lookup_uint64_array(oldnv, ZPOOL_CONFIG_STATS,
+ (uint64_t **)&oldvs, &c) == 0);
+ } else {
+ oldvs = &zerovs;
+ }
+
+ verify(nvlist_lookup_uint64_array(newnv, ZPOOL_CONFIG_STATS,
+ (uint64_t **)&newvs, &c) == 0);
+
+ if (strlen(name) + depth > cb->cb_namewidth)
+ (void) printf("%*s%s", depth, "", name);
+ else
+ (void) printf("%*s%s%*s", depth, "", name,
+ (int)(cb->cb_namewidth - strlen(name) - depth), "");
+
+ tdelta = newvs->vs_timestamp - oldvs->vs_timestamp;
+
+ if (tdelta == 0)
+ scale = 1.0;
+ else
+ scale = (double)NANOSEC / tdelta;
+
+ /* only toplevel vdevs have capacity stats */
+ if (newvs->vs_space == 0) {
+ (void) printf(" - -");
+ } else {
+ print_one_stat(newvs->vs_alloc);
+ print_one_stat(newvs->vs_space - newvs->vs_alloc);
+ }
+
+ print_one_stat((uint64_t)(scale * (newvs->vs_ops[ZIO_TYPE_READ] -
+ oldvs->vs_ops[ZIO_TYPE_READ])));
+
+ print_one_stat((uint64_t)(scale * (newvs->vs_ops[ZIO_TYPE_WRITE] -
+ oldvs->vs_ops[ZIO_TYPE_WRITE])));
+
+ print_one_stat((uint64_t)(scale * (newvs->vs_bytes[ZIO_TYPE_READ] -
+ oldvs->vs_bytes[ZIO_TYPE_READ])));
+
+ print_one_stat((uint64_t)(scale * (newvs->vs_bytes[ZIO_TYPE_WRITE] -
+ oldvs->vs_bytes[ZIO_TYPE_WRITE])));
+
+ (void) printf("\n");
+
+ if (!cb->cb_verbose)
+ return;
+
+ if (nvlist_lookup_nvlist_array(newnv, ZPOOL_CONFIG_CHILDREN,
+ &newchild, &children) != 0)
+ return;
+
+ if (oldnv && nvlist_lookup_nvlist_array(oldnv, ZPOOL_CONFIG_CHILDREN,
+ &oldchild, &c) != 0)
+ return;
+
+ for (c = 0; c < children; c++) {
+ vname = zpool_vdev_name(g_zfs, zhp, newchild[c]);
+ print_vdev_stats(zhp, vname, oldnv ? oldchild[c] : NULL,
+ newchild[c], cb, depth + 2);
+ free(vname);
+ }
+}
+
+static int
+refresh_iostat(zpool_handle_t *zhp, void *data)
+{
+ iostat_cbdata_t *cb = data;
+ boolean_t missing;
+
+ /*
+ * If the pool has disappeared, remove it from the list and continue.
+ */
+ if (zpool_refresh_stats(zhp, &missing) != 0)
+ return (-1);
+
+ if (missing)
+ pool_list_remove(cb->cb_list, zhp);
+
+ return (0);
+}
+
+/*
+ * Callback to print out the iostats for the given pool.
+ */
+int
+print_iostat(zpool_handle_t *zhp, void *data)
+{
+ iostat_cbdata_t *cb = data;
+ nvlist_t *oldconfig, *newconfig;
+ nvlist_t *oldnvroot, *newnvroot;
+
+ newconfig = zpool_get_config(zhp, &oldconfig);
+
+ if (cb->cb_iteration == 1)
+ oldconfig = NULL;
+
+ verify(nvlist_lookup_nvlist(newconfig, ZPOOL_CONFIG_VDEV_TREE,
+ &newnvroot) == 0);
+
+ if (oldconfig == NULL)
+ oldnvroot = NULL;
+ else
+ verify(nvlist_lookup_nvlist(oldconfig, ZPOOL_CONFIG_VDEV_TREE,
+ &oldnvroot) == 0);
+
+ /*
+ * Print out the statistics for the pool.
+ */
+ print_vdev_stats(zhp, zpool_get_name(zhp), oldnvroot, newnvroot, cb, 0);
+
+ if (cb->cb_verbose)
+ print_iostat_separator(cb);
+
+ return (0);
+}
+
+int
+get_namewidth(zpool_handle_t *zhp, void *data)
+{
+ iostat_cbdata_t *cb = data;
+ nvlist_t *config, *nvroot;
+
+ if ((config = zpool_get_config(zhp, NULL)) != NULL) {
+ verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
+ &nvroot) == 0);
+ if (!cb->cb_verbose)
+ cb->cb_namewidth = strlen(zpool_get_name(zhp));
+ else
+ cb->cb_namewidth = max_width(zhp, nvroot, 0, 0);
+ }
+
+ /*
+ * The width must fall into the range [10,38]. The upper limit is the
+ * maximum we can have and still fit in 80 columns.
+ */
+ if (cb->cb_namewidth < 10)
+ cb->cb_namewidth = 10;
+ if (cb->cb_namewidth > 38)
+ cb->cb_namewidth = 38;
+
+ return (0);
+}
+
+/*
+ * zpool iostat [-v] [pool] ... [interval [count]]
+ *
+ * -v Display statistics for individual vdevs
+ *
+ * This command can be tricky because we want to be able to deal with pool
+ * creation/destruction as well as vdev configuration changes. The bulk of this
+ * processing is handled by the pool_list_* routines in zpool_iter.c. We rely
+ * on pool_list_update() to detect the addition of new pools. Configuration
+ * changes are all handled within libzfs.
+ */
+int
+zpool_do_iostat(int argc, char **argv)
+{
+ int c;
+ int ret;
+ int npools;
+ unsigned long interval = 0, count = 0;
+ zpool_list_t *list;
+ boolean_t verbose = B_FALSE;
+ iostat_cbdata_t cb;
+
+ /* check options */
+ while ((c = getopt(argc, argv, "v")) != -1) {
+ switch (c) {
+ case 'v':
+ verbose = B_TRUE;
+ break;
+ case '?':
+ (void) fprintf(stderr, gettext("invalid option '%c'\n"),
+ optopt);
+ usage(B_FALSE);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ /*
+ * Determine if the last argument is an integer or a pool name
+ */
+ if (argc > 0 && isdigit(argv[argc - 1][0])) {
+ char *end;
+
+ errno = 0;
+ interval = strtoul(argv[argc - 1], &end, 10);
+
+ if (*end == '\0' && errno == 0) {
+ if (interval == 0) {
+ (void) fprintf(stderr, gettext("interval "
+ "cannot be zero\n"));
+ usage(B_FALSE);
+ }
+
+ /*
+ * Ignore the last parameter
+ */
+ argc--;
+ } else {
+ /*
+ * If this is not a valid number, just plow on. The
+ * user will get a more informative error message later
+ * on.
+ */
+ interval = 0;
+ }
+ }
+
+ /*
+ * If the last argument is also an integer, then we have both a count
+ * and an integer.
+ */
+ if (argc > 0 && isdigit(argv[argc - 1][0])) {
+ char *end;
+
+ errno = 0;
+ count = interval;
+ interval = strtoul(argv[argc - 1], &end, 10);
+
+ if (*end == '\0' && errno == 0) {
+ if (interval == 0) {
+ (void) fprintf(stderr, gettext("interval "
+ "cannot be zero\n"));
+ usage(B_FALSE);
+ }
+
+ /*
+ * Ignore the last parameter
+ */
+ argc--;
+ } else {
+ interval = 0;
+ }
+ }
+
+ /*
+ * Construct the list of all interesting pools.
+ */
+ ret = 0;
+ if ((list = pool_list_get(argc, argv, NULL, &ret)) == NULL)
+ return (1);
+
+ if (pool_list_count(list) == 0 && argc != 0) {
+ pool_list_free(list);
+ return (1);
+ }
+
+ if (pool_list_count(list) == 0 && interval == 0) {
+ pool_list_free(list);
+ (void) fprintf(stderr, gettext("no pools available\n"));
+ return (1);
+ }
+
+ /*
+ * Enter the main iostat loop.
+ */
+ cb.cb_list = list;
+ cb.cb_verbose = verbose;
+ cb.cb_iteration = 0;
+ cb.cb_namewidth = 0;
+
+ for (;;) {
+ pool_list_update(list);
+
+ if ((npools = pool_list_count(list)) == 0)
+ break;
+
+ /*
+ * Refresh all statistics. This is done as an explicit step
+ * before calculating the maximum name width, so that any
+ * configuration changes are properly accounted for.
+ */
+ (void) pool_list_iter(list, B_FALSE, refresh_iostat, &cb);
+
+ /*
+ * Iterate over all pools to determine the maximum width
+ * for the pool / device name column across all pools.
+ */
+ cb.cb_namewidth = 0;
+ (void) pool_list_iter(list, B_FALSE, get_namewidth, &cb);
+
+ /*
+ * If it's the first time, or verbose mode, print the header.
+ */
+ if (++cb.cb_iteration == 1 || verbose)
+ print_iostat_header(&cb);
+
+ (void) pool_list_iter(list, B_FALSE, print_iostat, &cb);
+
+ /*
+ * If there's more than one pool, and we're not in verbose mode
+ * (which prints a separator for us), then print a separator.
+ */
+ if (npools > 1 && !verbose)
+ print_iostat_separator(&cb);
+
+ if (verbose)
+ (void) printf("\n");
+
+ /*
+ * Flush the output so that redirection to a file isn't buffered
+ * indefinitely.
+ */
+ (void) fflush(stdout);
+
+ if (interval == 0)
+ break;
+
+ if (count != 0 && --count == 0)
+ break;
+
+ (void) sleep(interval);
+ }
+
+ pool_list_free(list);
+
+ return (ret);
+}
+
+typedef struct list_cbdata {
+ boolean_t cb_scripted;
+ boolean_t cb_first;
+ int cb_fields[MAX_FIELDS];
+ int cb_fieldcount;
+} list_cbdata_t;
+
+/*
+ * Given a list of columns to display, output appropriate headers for each one.
+ */
+void
+print_header(int *fields, size_t count)
+{
+ int i;
+ column_def_t *col;
+ const char *fmt;
+
+ for (i = 0; i < count; i++) {
+ col = &column_table[fields[i]];
+ if (i != 0)
+ (void) printf(" ");
+ if (col->cd_justify == left_justify)
+ fmt = "%-*s";
+ else
+ fmt = "%*s";
+
+ (void) printf(fmt, i == count - 1 ? strlen(col->cd_title) :
+ col->cd_width, col->cd_title);
+ }
+
+ (void) printf("\n");
+}
+
+int
+list_callback(zpool_handle_t *zhp, void *data)
+{
+ list_cbdata_t *cbp = data;
+ nvlist_t *config;
+ int i;
+ char buf[ZPOOL_MAXNAMELEN];
+ uint64_t total;
+ uint64_t used;
+ const char *fmt;
+ column_def_t *col;
+
+ if (cbp->cb_first) {
+ if (!cbp->cb_scripted)
+ print_header(cbp->cb_fields, cbp->cb_fieldcount);
+ cbp->cb_first = B_FALSE;
+ }
+
+ if (zpool_get_state(zhp) == POOL_STATE_UNAVAIL) {
+ config = NULL;
+ } else {
+ config = zpool_get_config(zhp, NULL);
+ total = zpool_get_space_total(zhp);
+ used = zpool_get_space_used(zhp);
+ }
+
+ for (i = 0; i < cbp->cb_fieldcount; i++) {
+ if (i != 0) {
+ if (cbp->cb_scripted)
+ (void) printf("\t");
+ else
+ (void) printf(" ");
+ }
+
+ col = &column_table[cbp->cb_fields[i]];
+
+ switch (cbp->cb_fields[i]) {
+ case ZPOOL_FIELD_NAME:
+ (void) strlcpy(buf, zpool_get_name(zhp), sizeof (buf));
+ break;
+
+ case ZPOOL_FIELD_SIZE:
+ if (config == NULL)
+ (void) strlcpy(buf, "-", sizeof (buf));
+ else
+ zfs_nicenum(total, buf, sizeof (buf));
+ break;
+
+ case ZPOOL_FIELD_USED:
+ if (config == NULL)
+ (void) strlcpy(buf, "-", sizeof (buf));
+ else
+ zfs_nicenum(used, buf, sizeof (buf));
+ break;
+
+ case ZPOOL_FIELD_AVAILABLE:
+ if (config == NULL)
+ (void) strlcpy(buf, "-", sizeof (buf));
+ else
+ zfs_nicenum(total - used, buf, sizeof (buf));
+ break;
+
+ case ZPOOL_FIELD_CAPACITY:
+ if (config == NULL) {
+ (void) strlcpy(buf, "-", sizeof (buf));
+ } else {
+ uint64_t capacity = (total == 0 ? 0 :
+ (used * 100 / total));
+ (void) snprintf(buf, sizeof (buf), "%llu%%",
+ (u_longlong_t)capacity);
+ }
+ break;
+
+ case ZPOOL_FIELD_HEALTH:
+ if (config == NULL) {
+ (void) strlcpy(buf, "FAULTED", sizeof (buf));
+ } else {
+ nvlist_t *nvroot;
+ vdev_stat_t *vs;
+ uint_t vsc;
+
+ verify(nvlist_lookup_nvlist(config,
+ ZPOOL_CONFIG_VDEV_TREE, &nvroot) == 0);
+ verify(nvlist_lookup_uint64_array(nvroot,
+ ZPOOL_CONFIG_STATS, (uint64_t **)&vs,
+ &vsc) == 0);
+ (void) strlcpy(buf, state_to_name(vs),
+ sizeof (buf));
+ }
+ break;
+
+ case ZPOOL_FIELD_ROOT:
+ if (config == NULL)
+ (void) strlcpy(buf, "-", sizeof (buf));
+ else if (zpool_get_root(zhp, buf, sizeof (buf)) != 0)
+ (void) strlcpy(buf, "-", sizeof (buf));
+ break;
+ }
+
+ if (cbp->cb_scripted)
+ (void) printf("%s", buf);
+ else {
+ if (col->cd_justify == left_justify)
+ fmt = "%-*s";
+ else
+ fmt = "%*s";
+
+ (void) printf(fmt, i == cbp->cb_fieldcount - 1 ?
+ strlen(buf) : col->cd_width, buf);
+ }
+ }
+
+ (void) printf("\n");
+
+ return (0);
+}
+
+/*
+ * zpool list [-H] [-o field[,field]*] [pool] ...
+ *
+ * -H Scripted mode. Don't display headers, and separate fields by
+ * a single tab.
+ * -o List of fields to display. Defaults to all fields, or
+ * "name,size,used,available,capacity,health,root"
+ *
+ * List all pools in the system, whether or not they're healthy. Output space
+ * statistics for each one, as well as health status summary.
+ */
+int
+zpool_do_list(int argc, char **argv)
+{
+ int c;
+ int ret;
+ list_cbdata_t cb = { 0 };
+ static char default_fields[] =
+ "name,size,used,available,capacity,health,root";
+ char *fields = default_fields;
+ char *value;
+
+ /* check options */
+ while ((c = getopt(argc, argv, ":Ho:")) != -1) {
+ switch (c) {
+ case 'H':
+ cb.cb_scripted = B_TRUE;
+ break;
+ case 'o':
+ fields = optarg;
+ break;
+ case ':':
+ (void) fprintf(stderr, gettext("missing argument for "
+ "'%c' option\n"), optopt);
+ usage(B_FALSE);
+ break;
+ case '?':
+ (void) fprintf(stderr, gettext("invalid option '%c'\n"),
+ optopt);
+ usage(B_FALSE);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ while (*fields != '\0') {
+ if (cb.cb_fieldcount == MAX_FIELDS) {
+ (void) fprintf(stderr, gettext("too many "
+ "properties given to -o option\n"));
+ usage(B_FALSE);
+ }
+
+ if ((cb.cb_fields[cb.cb_fieldcount] = getsubopt(&fields,
+ column_subopts, &value)) == -1) {
+ (void) fprintf(stderr, gettext("invalid property "
+ "'%s'\n"), value);
+ usage(B_FALSE);
+ }
+
+ cb.cb_fieldcount++;
+ }
+
+
+ cb.cb_first = B_TRUE;
+
+ ret = for_each_pool(argc, argv, B_TRUE, NULL, list_callback, &cb);
+
+ if (argc == 0 && cb.cb_first) {
+ (void) printf(gettext("no pools available\n"));
+ return (0);
+ }
+
+ return (ret);
+}
+
+static nvlist_t *
+zpool_get_vdev_by_name(nvlist_t *nv, char *name)
+{
+ nvlist_t **child;
+ uint_t c, children;
+ nvlist_t *match;
+ char *path;
+
+ if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
+ &child, &children) != 0) {
+ verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path) == 0);
+ if (strncmp(name, _PATH_DEV, sizeof(_PATH_DEV)-1) == 0)
+ name += sizeof(_PATH_DEV)-1;
+ if (strncmp(path, _PATH_DEV, sizeof(_PATH_DEV)-1) == 0)
+ path += sizeof(_PATH_DEV)-1;
+ if (strcmp(name, path) == 0)
+ return (nv);
+ return (NULL);
+ }
+
+ for (c = 0; c < children; c++)
+ if ((match = zpool_get_vdev_by_name(child[c], name)) != NULL)
+ return (match);
+
+ return (NULL);
+}
+
+static int
+zpool_do_attach_or_replace(int argc, char **argv, int replacing)
+{
+ boolean_t force = B_FALSE;
+ int c;
+ nvlist_t *nvroot;
+ char *poolname, *old_disk, *new_disk;
+ zpool_handle_t *zhp;
+ nvlist_t *config;
+ int ret;
+ int log_argc;
+ char **log_argv;
+
+ /* check options */
+ while ((c = getopt(argc, argv, "f")) != -1) {
+ switch (c) {
+ case 'f':
+ force = B_TRUE;
+ break;
+ case '?':
+ (void) fprintf(stderr, gettext("invalid option '%c'\n"),
+ optopt);
+ usage(B_FALSE);
+ }
+ }
+
+ log_argc = argc;
+ log_argv = argv;
+ argc -= optind;
+ argv += optind;
+
+ /* get pool name and check number of arguments */
+ if (argc < 1) {
+ (void) fprintf(stderr, gettext("missing pool name argument\n"));
+ usage(B_FALSE);
+ }
+
+ poolname = argv[0];
+
+ if (argc < 2) {
+ (void) fprintf(stderr,
+ gettext("missing <device> specification\n"));
+ usage(B_FALSE);
+ }
+
+ old_disk = argv[1];
+
+ if (argc < 3) {
+ if (!replacing) {
+ (void) fprintf(stderr,
+ gettext("missing <new_device> specification\n"));
+ usage(B_FALSE);
+ }
+ new_disk = old_disk;
+ argc -= 1;
+ argv += 1;
+ } else {
+ new_disk = argv[2];
+ argc -= 2;
+ argv += 2;
+ }
+
+ if (argc > 1) {
+ (void) fprintf(stderr, gettext("too many arguments\n"));
+ usage(B_FALSE);
+ }
+
+ if ((zhp = zpool_open(g_zfs, poolname)) == NULL)
+ return (1);
+
+ if ((config = zpool_get_config(zhp, NULL)) == NULL) {
+ (void) fprintf(stderr, gettext("pool '%s' is unavailable\n"),
+ poolname);
+ zpool_close(zhp);
+ return (1);
+ }
+
+ nvroot = make_root_vdev(config, force, B_FALSE, replacing, argc, argv);
+ if (nvroot == NULL) {
+ zpool_close(zhp);
+ return (1);
+ }
+
+ ret = zpool_vdev_attach(zhp, old_disk, new_disk, nvroot, replacing);
+
+ if (!ret) {
+ zpool_log_history(g_zfs, log_argc, log_argv, poolname, B_TRUE,
+ B_FALSE);
+ }
+
+ nvlist_free(nvroot);
+ zpool_close(zhp);
+
+ return (ret);
+}
+
+/*
+ * zpool replace [-f] <pool> <device> <new_device>
+ *
+ * -f Force attach, even if <new_device> appears to be in use.
+ *
+ * Replace <device> with <new_device>.
+ */
+/* ARGSUSED */
+int
+zpool_do_replace(int argc, char **argv)
+{
+ return (zpool_do_attach_or_replace(argc, argv, B_TRUE));
+}
+
+/*
+ * zpool attach [-f] <pool> <device> <new_device>
+ *
+ * -f Force attach, even if <new_device> appears to be in use.
+ *
+ * Attach <new_device> to the mirror containing <device>. If <device> is not
+ * part of a mirror, then <device> will be transformed into a mirror of
+ * <device> and <new_device>. In either case, <new_device> will begin life
+ * with a DTL of [0, now], and will immediately begin to resilver itself.
+ */
+int
+zpool_do_attach(int argc, char **argv)
+{
+ return (zpool_do_attach_or_replace(argc, argv, B_FALSE));
+}
+
+/*
+ * zpool detach [-f] <pool> <device>
+ *
+ * -f Force detach of <device>, even if DTLs argue against it
+ * (not supported yet)
+ *
+ * Detach a device from a mirror. The operation will be refused if <device>
+ * is the last device in the mirror, or if the DTLs indicate that this device
+ * has the only valid copy of some data.
+ */
+/* ARGSUSED */
+int
+zpool_do_detach(int argc, char **argv)
+{
+ int c;
+ char *poolname, *path;
+ zpool_handle_t *zhp;
+ int ret;
+
+ /* check options */
+ while ((c = getopt(argc, argv, "f")) != -1) {
+ switch (c) {
+ case 'f':
+ case '?':
+ (void) fprintf(stderr, gettext("invalid option '%c'\n"),
+ optopt);
+ usage(B_FALSE);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ /* get pool name and check number of arguments */
+ if (argc < 1) {
+ (void) fprintf(stderr, gettext("missing pool name argument\n"));
+ usage(B_FALSE);
+ }
+
+ if (argc < 2) {
+ (void) fprintf(stderr,
+ gettext("missing <device> specification\n"));
+ usage(B_FALSE);
+ }
+
+ poolname = argv[0];
+ path = argv[1];
+
+ if ((zhp = zpool_open(g_zfs, poolname)) == NULL)
+ return (1);
+
+ ret = zpool_vdev_detach(zhp, path);
+
+ if (!ret) {
+ zpool_log_history(g_zfs, argc + optind, argv - optind, poolname,
+ B_TRUE, B_FALSE);
+ }
+ zpool_close(zhp);
+
+ return (ret);
+}
+
+/*
+ * zpool online <pool> <device> ...
+ */
+/* ARGSUSED */
+int
+zpool_do_online(int argc, char **argv)
+{
+ int c, i;
+ char *poolname;
+ zpool_handle_t *zhp;
+ int ret = 0;
+
+ /* check options */
+ while ((c = getopt(argc, argv, "t")) != -1) {
+ switch (c) {
+ case 't':
+ case '?':
+ (void) fprintf(stderr, gettext("invalid option '%c'\n"),
+ optopt);
+ usage(B_FALSE);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ /* get pool name and check number of arguments */
+ if (argc < 1) {
+ (void) fprintf(stderr, gettext("missing pool name\n"));
+ usage(B_FALSE);
+ }
+ if (argc < 2) {
+ (void) fprintf(stderr, gettext("missing device name\n"));
+ usage(B_FALSE);
+ }
+
+ poolname = argv[0];
+
+ if ((zhp = zpool_open(g_zfs, poolname)) == NULL)
+ return (1);
+
+ for (i = 1; i < argc; i++)
+ if (zpool_vdev_online(zhp, argv[i]) == 0)
+ (void) printf(gettext("Bringing device %s online\n"),
+ argv[i]);
+ else
+ ret = 1;
+
+ if (!ret) {
+ zpool_log_history(g_zfs, argc + optind, argv - optind, poolname,
+ B_TRUE, B_FALSE);
+ }
+ zpool_close(zhp);
+
+ return (ret);
+}
+
+/*
+ * zpool offline [-ft] <pool> <device> ...
+ *
+ * -f Force the device into the offline state, even if doing
+ * so would appear to compromise pool availability.
+ * (not supported yet)
+ *
+ * -t Only take the device off-line temporarily. The offline
+ * state will not be persistent across reboots.
+ */
+/* ARGSUSED */
+int
+zpool_do_offline(int argc, char **argv)
+{
+ int c, i;
+ char *poolname;
+ zpool_handle_t *zhp;
+ int ret = 0;
+ boolean_t istmp = B_FALSE;
+
+ /* check options */
+ while ((c = getopt(argc, argv, "ft")) != -1) {
+ switch (c) {
+ case 't':
+ istmp = B_TRUE;
+ break;
+ case 'f':
+ case '?':
+ (void) fprintf(stderr, gettext("invalid option '%c'\n"),
+ optopt);
+ usage(B_FALSE);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ /* get pool name and check number of arguments */
+ if (argc < 1) {
+ (void) fprintf(stderr, gettext("missing pool name\n"));
+ usage(B_FALSE);
+ }
+ if (argc < 2) {
+ (void) fprintf(stderr, gettext("missing device name\n"));
+ usage(B_FALSE);
+ }
+
+ poolname = argv[0];
+
+ if ((zhp = zpool_open(g_zfs, poolname)) == NULL)
+ return (1);
+
+ for (i = 1; i < argc; i++)
+ if (zpool_vdev_offline(zhp, argv[i], istmp) == 0)
+ (void) printf(gettext("Bringing device %s offline\n"),
+ argv[i]);
+ else
+ ret = 1;
+
+ if (!ret) {
+ zpool_log_history(g_zfs, argc + optind, argv - optind, poolname,
+ B_TRUE, B_FALSE);
+ }
+ zpool_close(zhp);
+
+ return (ret);
+}
+
+/*
+ * zpool clear <pool> [device]
+ *
+ * Clear all errors associated with a pool or a particular device.
+ */
+int
+zpool_do_clear(int argc, char **argv)
+{
+ int ret = 0;
+ zpool_handle_t *zhp;
+ char *pool, *device;
+
+ if (argc < 2) {
+ (void) fprintf(stderr, gettext("missing pool name\n"));
+ usage(B_FALSE);
+ }
+
+ if (argc > 3) {
+ (void) fprintf(stderr, gettext("too many arguments\n"));
+ usage(B_FALSE);
+ }
+
+ pool = argv[1];
+ device = argc == 3 ? argv[2] : NULL;
+
+ if ((zhp = zpool_open(g_zfs, pool)) == NULL)
+ return (1);
+
+ if (zpool_clear(zhp, device) != 0)
+ ret = 1;
+
+ if (!ret)
+ zpool_log_history(g_zfs, argc, argv, pool, B_TRUE, B_FALSE);
+ zpool_close(zhp);
+
+ return (ret);
+}
+
+typedef struct scrub_cbdata {
+ int cb_type;
+ int cb_argc;
+ char **cb_argv;
+} scrub_cbdata_t;
+
+int
+scrub_callback(zpool_handle_t *zhp, void *data)
+{
+ scrub_cbdata_t *cb = data;
+ int err;
+
+ /*
+ * Ignore faulted pools.
+ */
+ if (zpool_get_state(zhp) == POOL_STATE_UNAVAIL) {
+ (void) fprintf(stderr, gettext("cannot scrub '%s': pool is "
+ "currently unavailable\n"), zpool_get_name(zhp));
+ return (1);
+ }
+
+ err = zpool_scrub(zhp, cb->cb_type);
+
+ if (!err) {
+ zpool_log_history(g_zfs, cb->cb_argc, cb->cb_argv,
+ zpool_get_name(zhp), B_TRUE, B_FALSE);
+ }
+
+ return (err != 0);
+}
+
+/*
+ * zpool scrub [-s] <pool> ...
+ *
+ * -s Stop. Stops any in-progress scrub.
+ */
+int
+zpool_do_scrub(int argc, char **argv)
+{
+ int c;
+ scrub_cbdata_t cb;
+
+ cb.cb_type = POOL_SCRUB_EVERYTHING;
+
+ /* check options */
+ while ((c = getopt(argc, argv, "s")) != -1) {
+ switch (c) {
+ case 's':
+ cb.cb_type = POOL_SCRUB_NONE;
+ break;
+ case '?':
+ (void) fprintf(stderr, gettext("invalid option '%c'\n"),
+ optopt);
+ usage(B_FALSE);
+ }
+ }
+
+ cb.cb_argc = argc;
+ cb.cb_argv = argv;
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ (void) fprintf(stderr, gettext("missing pool name argument\n"));
+ usage(B_FALSE);
+ }
+
+ return (for_each_pool(argc, argv, B_TRUE, NULL, scrub_callback, &cb));
+}
+
+typedef struct status_cbdata {
+ int cb_count;
+ boolean_t cb_allpools;
+ boolean_t cb_verbose;
+ boolean_t cb_explain;
+ boolean_t cb_first;
+} status_cbdata_t;
+
+/*
+ * Print out detailed scrub status.
+ */
+void
+print_scrub_status(nvlist_t *nvroot)
+{
+ vdev_stat_t *vs;
+ uint_t vsc;
+ time_t start, end, now;
+ double fraction_done;
+ uint64_t examined, total, minutes_left;
+ char *scrub_type;
+
+ verify(nvlist_lookup_uint64_array(nvroot, ZPOOL_CONFIG_STATS,
+ (uint64_t **)&vs, &vsc) == 0);
+
+ /*
+ * If there's never been a scrub, there's not much to say.
+ */
+ if (vs->vs_scrub_end == 0 && vs->vs_scrub_type == POOL_SCRUB_NONE) {
+ (void) printf(gettext("none requested\n"));
+ return;
+ }
+
+ scrub_type = (vs->vs_scrub_type == POOL_SCRUB_RESILVER) ?
+ "resilver" : "scrub";
+
+ start = vs->vs_scrub_start;
+ end = vs->vs_scrub_end;
+ now = time(NULL);
+ examined = vs->vs_scrub_examined;
+ total = vs->vs_alloc;
+
+ if (end != 0) {
+ (void) printf(gettext("%s %s with %llu errors on %s"),
+ scrub_type, vs->vs_scrub_complete ? "completed" : "stopped",
+ (u_longlong_t)vs->vs_scrub_errors, ctime(&end));
+ return;
+ }
+
+ if (examined == 0)
+ examined = 1;
+ if (examined > total)
+ total = examined;
+
+ fraction_done = (double)examined / total;
+ minutes_left = (uint64_t)((now - start) *
+ (1 - fraction_done) / fraction_done / 60);
+
+ (void) printf(gettext("%s in progress, %.2f%% done, %lluh%um to go\n"),
+ scrub_type, 100 * fraction_done,
+ (u_longlong_t)(minutes_left / 60), (uint_t)(minutes_left % 60));
+}
+
+typedef struct spare_cbdata {
+ uint64_t cb_guid;
+ zpool_handle_t *cb_zhp;
+} spare_cbdata_t;
+
+static boolean_t
+find_vdev(nvlist_t *nv, uint64_t search)
+{
+ uint64_t guid;
+ nvlist_t **child;
+ uint_t c, children;
+
+ if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID, &guid) == 0 &&
+ search == guid)
+ return (B_TRUE);
+
+ if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
+ &child, &children) == 0) {
+ for (c = 0; c < children; c++)
+ if (find_vdev(child[c], search))
+ return (B_TRUE);
+ }
+
+ return (B_FALSE);
+}
+
+static int
+find_spare(zpool_handle_t *zhp, void *data)
+{
+ spare_cbdata_t *cbp = data;
+ nvlist_t *config, *nvroot;
+
+ config = zpool_get_config(zhp, NULL);
+ verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
+ &nvroot) == 0);
+
+ if (find_vdev(nvroot, cbp->cb_guid)) {
+ cbp->cb_zhp = zhp;
+ return (1);
+ }
+
+ zpool_close(zhp);
+ return (0);
+}
+
+/*
+ * Print out configuration state as requested by status_callback.
+ */
+void
+print_status_config(zpool_handle_t *zhp, const char *name, nvlist_t *nv,
+ int namewidth, int depth, boolean_t isspare)
+{
+ nvlist_t **child;
+ uint_t c, children;
+ vdev_stat_t *vs;
+ char rbuf[6], wbuf[6], cbuf[6], repaired[7];
+ char *vname;
+ uint64_t notpresent;
+ spare_cbdata_t cb;
+ const char *state;
+
+ verify(nvlist_lookup_uint64_array(nv, ZPOOL_CONFIG_STATS,
+ (uint64_t **)&vs, &c) == 0);
+
+ if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
+ &child, &children) != 0)
+ children = 0;
+
+ state = state_to_name(vs);
+ if (isspare) {
+ /*
+ * For hot spares, we use the terms 'INUSE' and 'AVAILABLE' for
+ * online drives.
+ */
+ if (vs->vs_aux == VDEV_AUX_SPARED)
+ state = "INUSE";
+ else if (vs->vs_state == VDEV_STATE_HEALTHY)
+ state = "AVAIL";
+ }
+
+ (void) printf("\t%*s%-*s %-8s", depth, "", namewidth - depth,
+ name, state);
+
+ if (!isspare) {
+ zfs_nicenum(vs->vs_read_errors, rbuf, sizeof (rbuf));
+ zfs_nicenum(vs->vs_write_errors, wbuf, sizeof (wbuf));
+ zfs_nicenum(vs->vs_checksum_errors, cbuf, sizeof (cbuf));
+ (void) printf(" %5s %5s %5s", rbuf, wbuf, cbuf);
+ }
+
+ if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_NOT_PRESENT,
+ &notpresent) == 0) {
+ char *path;
+ verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path) == 0);
+ (void) printf(" was %s", path);
+ } else if (vs->vs_aux != 0) {
+ (void) printf(" ");
+
+ switch (vs->vs_aux) {
+ case VDEV_AUX_OPEN_FAILED:
+ (void) printf(gettext("cannot open"));
+ break;
+
+ case VDEV_AUX_BAD_GUID_SUM:
+ (void) printf(gettext("missing device"));
+ break;
+
+ case VDEV_AUX_NO_REPLICAS:
+ (void) printf(gettext("insufficient replicas"));
+ break;
+
+ case VDEV_AUX_VERSION_NEWER:
+ (void) printf(gettext("newer version"));
+ break;
+
+ case VDEV_AUX_SPARED:
+ verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID,
+ &cb.cb_guid) == 0);
+ if (zpool_iter(g_zfs, find_spare, &cb) == 1) {
+ if (strcmp(zpool_get_name(cb.cb_zhp),
+ zpool_get_name(zhp)) == 0)
+ (void) printf(gettext("currently in "
+ "use"));
+ else
+ (void) printf(gettext("in use by "
+ "pool '%s'"),
+ zpool_get_name(cb.cb_zhp));
+ zpool_close(cb.cb_zhp);
+ } else {
+ (void) printf(gettext("currently in use"));
+ }
+ break;
+
+ default:
+ (void) printf(gettext("corrupted data"));
+ break;
+ }
+ } else if (vs->vs_scrub_repaired != 0 && children == 0) {
+ /*
+ * Report bytes resilvered/repaired on leaf devices.
+ */
+ zfs_nicenum(vs->vs_scrub_repaired, repaired, sizeof (repaired));
+ (void) printf(gettext(" %s %s"), repaired,
+ (vs->vs_scrub_type == POOL_SCRUB_RESILVER) ?
+ "resilvered" : "repaired");
+ }
+
+ (void) printf("\n");
+
+ for (c = 0; c < children; c++) {
+ vname = zpool_vdev_name(g_zfs, zhp, child[c]);
+ print_status_config(zhp, vname, child[c],
+ namewidth, depth + 2, isspare);
+ free(vname);
+ }
+}
+
+static void
+print_error_log(zpool_handle_t *zhp)
+{
+ nvlist_t *nverrlist;
+ nvpair_t *elem;
+ char *pathname;
+ size_t len = MAXPATHLEN * 2;
+
+ if (zpool_get_errlog(zhp, &nverrlist) != 0) {
+ (void) printf("errors: List of errors unavailable "
+ "(insufficient privileges)\n");
+ return;
+ }
+
+ (void) printf("errors: Permanent errors have been "
+ "detected in the following files:\n\n");
+
+ pathname = safe_malloc(len);
+ elem = NULL;
+ while ((elem = nvlist_next_nvpair(nverrlist, elem)) != NULL) {
+ nvlist_t *nv;
+ uint64_t dsobj, obj;
+
+ verify(nvpair_value_nvlist(elem, &nv) == 0);
+ verify(nvlist_lookup_uint64(nv, ZPOOL_ERR_DATASET,
+ &dsobj) == 0);
+ verify(nvlist_lookup_uint64(nv, ZPOOL_ERR_OBJECT,
+ &obj) == 0);
+ zpool_obj_to_path(zhp, dsobj, obj, pathname, len);
+ (void) printf("%7s %s\n", "", pathname);
+ }
+ free(pathname);
+ nvlist_free(nverrlist);
+}
+
+static void
+print_spares(zpool_handle_t *zhp, nvlist_t **spares, uint_t nspares,
+ int namewidth)
+{
+ uint_t i;
+ char *name;
+
+ if (nspares == 0)
+ return;
+
+ (void) printf(gettext("\tspares\n"));
+
+ for (i = 0; i < nspares; i++) {
+ name = zpool_vdev_name(g_zfs, zhp, spares[i]);
+ print_status_config(zhp, name, spares[i],
+ namewidth, 2, B_TRUE);
+ free(name);
+ }
+}
+
+/*
+ * Display a summary of pool status. Displays a summary such as:
+ *
+ * pool: tank
+ * status: DEGRADED
+ * reason: One or more devices ...
+ * see: http://www.sun.com/msg/ZFS-xxxx-01
+ * config:
+ * mirror DEGRADED
+ * c1t0d0 OK
+ * c2t0d0 UNAVAIL
+ *
+ * When given the '-v' option, we print out the complete config. If the '-e'
+ * option is specified, then we print out error rate information as well.
+ */
+int
+status_callback(zpool_handle_t *zhp, void *data)
+{
+ status_cbdata_t *cbp = data;
+ nvlist_t *config, *nvroot;
+ char *msgid;
+ int reason;
+ const char *health;
+ uint_t c;
+ vdev_stat_t *vs;
+
+ config = zpool_get_config(zhp, NULL);
+ reason = zpool_get_status(zhp, &msgid);
+
+ cbp->cb_count++;
+
+ /*
+ * If we were given 'zpool status -x', only report those pools with
+ * problems.
+ */
+ if (reason == ZPOOL_STATUS_OK && cbp->cb_explain) {
+ if (!cbp->cb_allpools) {
+ (void) printf(gettext("pool '%s' is healthy\n"),
+ zpool_get_name(zhp));
+ if (cbp->cb_first)
+ cbp->cb_first = B_FALSE;
+ }
+ return (0);
+ }
+
+ if (cbp->cb_first)
+ cbp->cb_first = B_FALSE;
+ else
+ (void) printf("\n");
+
+ verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
+ &nvroot) == 0);
+ verify(nvlist_lookup_uint64_array(nvroot, ZPOOL_CONFIG_STATS,
+ (uint64_t **)&vs, &c) == 0);
+ health = state_to_name(vs);
+
+ (void) printf(gettext(" pool: %s\n"), zpool_get_name(zhp));
+ (void) printf(gettext(" state: %s\n"), health);
+
+ switch (reason) {
+ case ZPOOL_STATUS_MISSING_DEV_R:
+ (void) printf(gettext("status: One or more devices could not "
+ "be opened. Sufficient replicas exist for\n\tthe pool to "
+ "continue functioning in a degraded state.\n"));
+ (void) printf(gettext("action: Attach the missing device and "
+ "online it using 'zpool online'.\n"));
+ break;
+
+ case ZPOOL_STATUS_MISSING_DEV_NR:
+ (void) printf(gettext("status: One or more devices could not "
+ "be opened. There are insufficient\n\treplicas for the "
+ "pool to continue functioning.\n"));
+ (void) printf(gettext("action: Attach the missing device and "
+ "online it using 'zpool online'.\n"));
+ break;
+
+ case ZPOOL_STATUS_CORRUPT_LABEL_R:
+ (void) printf(gettext("status: One or more devices could not "
+ "be used because the label is missing or\n\tinvalid. "
+ "Sufficient replicas exist for the pool to continue\n\t"
+ "functioning in a degraded state.\n"));
+ (void) printf(gettext("action: Replace the device using "
+ "'zpool replace'.\n"));
+ break;
+
+ case ZPOOL_STATUS_CORRUPT_LABEL_NR:
+ (void) printf(gettext("status: One or more devices could not "
+ "be used because the label is missing \n\tor invalid. "
+ "There are insufficient replicas for the pool to "
+ "continue\n\tfunctioning.\n"));
+ (void) printf(gettext("action: Destroy and re-create the pool "
+ "from a backup source.\n"));
+ break;
+
+ case ZPOOL_STATUS_FAILING_DEV:
+ (void) printf(gettext("status: One or more devices has "
+ "experienced an unrecoverable error. An\n\tattempt was "
+ "made to correct the error. Applications are "
+ "unaffected.\n"));
+ (void) printf(gettext("action: Determine if the device needs "
+ "to be replaced, and clear the errors\n\tusing "
+ "'zpool clear' or replace the device with 'zpool "
+ "replace'.\n"));
+ break;
+
+ case ZPOOL_STATUS_OFFLINE_DEV:
+ (void) printf(gettext("status: One or more devices has "
+ "been taken offline by the administrator.\n\tSufficient "
+ "replicas exist for the pool to continue functioning in "
+ "a\n\tdegraded state.\n"));
+ (void) printf(gettext("action: Online the device using "
+ "'zpool online' or replace the device with\n\t'zpool "
+ "replace'.\n"));
+ break;
+
+ case ZPOOL_STATUS_RESILVERING:
+ (void) printf(gettext("status: One or more devices is "
+ "currently being resilvered. The pool will\n\tcontinue "
+ "to function, possibly in a degraded state.\n"));
+ (void) printf(gettext("action: Wait for the resilver to "
+ "complete.\n"));
+ break;
+
+ case ZPOOL_STATUS_CORRUPT_DATA:
+ (void) printf(gettext("status: One or more devices has "
+ "experienced an error resulting in data\n\tcorruption. "
+ "Applications may be affected.\n"));
+ (void) printf(gettext("action: Restore the file in question "
+ "if possible. Otherwise restore the\n\tentire pool from "
+ "backup.\n"));
+ break;
+
+ case ZPOOL_STATUS_CORRUPT_POOL:
+ (void) printf(gettext("status: The pool metadata is corrupted "
+ "and the pool cannot be opened.\n"));
+ (void) printf(gettext("action: Destroy and re-create the pool "
+ "from a backup source.\n"));
+ break;
+
+ case ZPOOL_STATUS_VERSION_OLDER:
+ (void) printf(gettext("status: The pool is formatted using an "
+ "older on-disk format. The pool can\n\tstill be used, but "
+ "some features are unavailable.\n"));
+ (void) printf(gettext("action: Upgrade the pool using 'zpool "
+ "upgrade'. Once this is done, the\n\tpool will no longer "
+ "be accessible on older software versions.\n"));
+ break;
+
+ case ZPOOL_STATUS_VERSION_NEWER:
+ (void) printf(gettext("status: The pool has been upgraded to a "
+ "newer, incompatible on-disk version.\n\tThe pool cannot "
+ "be accessed on this system.\n"));
+ (void) printf(gettext("action: Access the pool from a system "
+ "running more recent software, or\n\trestore the pool from "
+ "backup.\n"));
+ break;
+
+ default:
+ /*
+ * The remaining errors can't actually be generated, yet.
+ */
+ assert(reason == ZPOOL_STATUS_OK);
+ }
+
+ if (msgid != NULL)
+ (void) printf(gettext(" see: http://www.sun.com/msg/%s\n"),
+ msgid);
+
+ if (config != NULL) {
+ int namewidth;
+ uint64_t nerr;
+ nvlist_t **spares;
+ uint_t nspares;
+
+
+ (void) printf(gettext(" scrub: "));
+ print_scrub_status(nvroot);
+
+ namewidth = max_width(zhp, nvroot, 0, 0);
+ if (namewidth < 10)
+ namewidth = 10;
+
+ (void) printf(gettext("config:\n\n"));
+ (void) printf(gettext("\t%-*s %-8s %5s %5s %5s\n"), namewidth,
+ "NAME", "STATE", "READ", "WRITE", "CKSUM");
+ print_status_config(zhp, zpool_get_name(zhp), nvroot,
+ namewidth, 0, B_FALSE);
+
+ if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_SPARES,
+ &spares, &nspares) == 0)
+ print_spares(zhp, spares, nspares, namewidth);
+
+ if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_ERRCOUNT,
+ &nerr) == 0) {
+ nvlist_t *nverrlist = NULL;
+
+ /*
+ * If the approximate error count is small, get a
+ * precise count by fetching the entire log and
+ * uniquifying the results.
+ */
+ if (nerr < 100 && !cbp->cb_verbose &&
+ zpool_get_errlog(zhp, &nverrlist) == 0) {
+ nvpair_t *elem;
+
+ elem = NULL;
+ nerr = 0;
+ while ((elem = nvlist_next_nvpair(nverrlist,
+ elem)) != NULL) {
+ nerr++;
+ }
+ }
+ nvlist_free(nverrlist);
+
+ (void) printf("\n");
+
+ if (nerr == 0)
+ (void) printf(gettext("errors: No known data "
+ "errors\n"));
+ else if (!cbp->cb_verbose)
+ (void) printf(gettext("errors: %llu data "
+ "errors, use '-v' for a list\n"),
+ (u_longlong_t)nerr);
+ else
+ print_error_log(zhp);
+ }
+ } else {
+ (void) printf(gettext("config: The configuration cannot be "
+ "determined.\n"));
+ }
+
+ return (0);
+}
+
+/*
+ * zpool status [-vx] [pool] ...
+ *
+ * -v Display complete error logs
+ * -x Display only pools with potential problems
+ *
+ * Describes the health status of all pools or some subset.
+ */
+int
+zpool_do_status(int argc, char **argv)
+{
+ int c;
+ int ret;
+ status_cbdata_t cb = { 0 };
+
+ /* check options */
+ while ((c = getopt(argc, argv, "vx")) != -1) {
+ switch (c) {
+ case 'v':
+ cb.cb_verbose = B_TRUE;
+ break;
+ case 'x':
+ cb.cb_explain = B_TRUE;
+ break;
+ case '?':
+ (void) fprintf(stderr, gettext("invalid option '%c'\n"),
+ optopt);
+ usage(B_FALSE);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ cb.cb_first = B_TRUE;
+
+ if (argc == 0)
+ cb.cb_allpools = B_TRUE;
+
+ ret = for_each_pool(argc, argv, B_TRUE, NULL, status_callback, &cb);
+
+ if (argc == 0 && cb.cb_count == 0)
+ (void) printf(gettext("no pools available\n"));
+ else if (cb.cb_explain && cb.cb_first && cb.cb_allpools)
+ (void) printf(gettext("all pools are healthy\n"));
+
+ return (ret);
+}
+
+typedef struct upgrade_cbdata {
+ int cb_all;
+ int cb_first;
+ int cb_newer;
+ int cb_argc;
+ char **cb_argv;
+} upgrade_cbdata_t;
+
+static int
+upgrade_cb(zpool_handle_t *zhp, void *arg)
+{
+ upgrade_cbdata_t *cbp = arg;
+ nvlist_t *config;
+ uint64_t version;
+ int ret = 0;
+
+ config = zpool_get_config(zhp, NULL);
+ verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_VERSION,
+ &version) == 0);
+
+ if (!cbp->cb_newer && version < ZFS_VERSION) {
+ if (!cbp->cb_all) {
+ if (cbp->cb_first) {
+ (void) printf(gettext("The following pools are "
+ "out of date, and can be upgraded. After "
+ "being\nupgraded, these pools will no "
+ "longer be accessible by older software "
+ "versions.\n\n"));
+ (void) printf(gettext("VER POOL\n"));
+ (void) printf(gettext("--- ------------\n"));
+ cbp->cb_first = B_FALSE;
+ }
+
+ (void) printf("%2llu %s\n", (u_longlong_t)version,
+ zpool_get_name(zhp));
+ } else {
+ cbp->cb_first = B_FALSE;
+ ret = zpool_upgrade(zhp);
+ if (!ret) {
+ zpool_log_history(g_zfs, cbp->cb_argc,
+ cbp->cb_argv, zpool_get_name(zhp), B_TRUE,
+ B_FALSE);
+ (void) printf(gettext("Successfully upgraded "
+ "'%s'\n"), zpool_get_name(zhp));
+ }
+ }
+ } else if (cbp->cb_newer && version > ZFS_VERSION) {
+ assert(!cbp->cb_all);
+
+ if (cbp->cb_first) {
+ (void) printf(gettext("The following pools are "
+ "formatted using a newer software version and\n"
+ "cannot be accessed on the current system.\n\n"));
+ (void) printf(gettext("VER POOL\n"));
+ (void) printf(gettext("--- ------------\n"));
+ cbp->cb_first = B_FALSE;
+ }
+
+ (void) printf("%2llu %s\n", (u_longlong_t)version,
+ zpool_get_name(zhp));
+ }
+
+ zpool_close(zhp);
+ return (ret);
+}
+
+/* ARGSUSED */
+static int
+upgrade_one(zpool_handle_t *zhp, void *data)
+{
+ nvlist_t *config;
+ uint64_t version;
+ int ret;
+ upgrade_cbdata_t *cbp = data;
+
+ config = zpool_get_config(zhp, NULL);
+ verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_VERSION,
+ &version) == 0);
+
+ if (version == ZFS_VERSION) {
+ (void) printf(gettext("Pool '%s' is already formatted "
+ "using the current version.\n"), zpool_get_name(zhp));
+ return (0);
+ }
+
+ ret = zpool_upgrade(zhp);
+
+ if (!ret) {
+ zpool_log_history(g_zfs, cbp->cb_argc, cbp->cb_argv,
+ zpool_get_name(zhp), B_TRUE, B_FALSE);
+ (void) printf(gettext("Successfully upgraded '%s' "
+ "from version %llu to version %llu\n"), zpool_get_name(zhp),
+ (u_longlong_t)version, (u_longlong_t)ZFS_VERSION);
+ }
+
+ return (ret != 0);
+}
+
+/*
+ * zpool upgrade
+ * zpool upgrade -v
+ * zpool upgrade <-a | pool>
+ *
+ * With no arguments, display downrev'd ZFS pool available for upgrade.
+ * Individual pools can be upgraded by specifying the pool, and '-a' will
+ * upgrade all pools.
+ */
+int
+zpool_do_upgrade(int argc, char **argv)
+{
+ int c;
+ upgrade_cbdata_t cb = { 0 };
+ int ret = 0;
+ boolean_t showversions = B_FALSE;
+
+ /* check options */
+ while ((c = getopt(argc, argv, "av")) != -1) {
+ switch (c) {
+ case 'a':
+ cb.cb_all = B_TRUE;
+ break;
+ case 'v':
+ showversions = B_TRUE;
+ break;
+ case '?':
+ (void) fprintf(stderr, gettext("invalid option '%c'\n"),
+ optopt);
+ usage(B_FALSE);
+ }
+ }
+
+ cb.cb_argc = argc;
+ cb.cb_argv = argv;
+ argc -= optind;
+ argv += optind;
+
+ if (showversions) {
+ if (cb.cb_all || argc != 0) {
+ (void) fprintf(stderr, gettext("-v option is "
+ "incompatible with other arguments\n"));
+ usage(B_FALSE);
+ }
+ } else if (cb.cb_all) {
+ if (argc != 0) {
+ (void) fprintf(stderr, gettext("-a option is "
+ "incompatible with other arguments\n"));
+ usage(B_FALSE);
+ }
+ }
+
+ (void) printf(gettext("This system is currently running ZFS version "
+ "%llu.\n\n"), ZFS_VERSION);
+ cb.cb_first = B_TRUE;
+ if (showversions) {
+ (void) printf(gettext("The following versions are "
+ "supported:\n\n"));
+ (void) printf(gettext("VER DESCRIPTION\n"));
+ (void) printf("--- -----------------------------------------"
+ "---------------\n");
+ (void) printf(gettext(" 1 Initial ZFS version\n"));
+ (void) printf(gettext(" 2 Ditto blocks "
+ "(replicated metadata)\n"));
+ (void) printf(gettext(" 3 Hot spares and double parity "
+ "RAID-Z\n"));
+ (void) printf(gettext(" 4 zpool history\n"));
+ (void) printf(gettext(" 5 Compression using the gzip "
+ "algorithm\n"));
+ (void) printf(gettext(" 6 bootfs pool property "));
+ (void) printf(gettext("\nFor more information on a particular "
+ "version, including supported releases, see:\n\n"));
+ (void) printf("http://www.opensolaris.org/os/community/zfs/"
+ "version/N\n\n");
+ (void) printf(gettext("Where 'N' is the version number.\n"));
+ } else if (argc == 0) {
+ int notfound;
+
+ ret = zpool_iter(g_zfs, upgrade_cb, &cb);
+ notfound = cb.cb_first;
+
+ if (!cb.cb_all && ret == 0) {
+ if (!cb.cb_first)
+ (void) printf("\n");
+ cb.cb_first = B_TRUE;
+ cb.cb_newer = B_TRUE;
+ ret = zpool_iter(g_zfs, upgrade_cb, &cb);
+ if (!cb.cb_first) {
+ notfound = B_FALSE;
+ (void) printf("\n");
+ }
+ }
+
+ if (ret == 0) {
+ if (notfound)
+ (void) printf(gettext("All pools are formatted "
+ "using this version.\n"));
+ else if (!cb.cb_all)
+ (void) printf(gettext("Use 'zpool upgrade -v' "
+ "for a list of available versions and "
+ "their associated\nfeatures.\n"));
+ }
+ } else {
+ ret = for_each_pool(argc, argv, B_FALSE, NULL,
+ upgrade_one, &cb);
+ }
+
+ return (ret);
+}
+
+/*
+ * Print out the command history for a specific pool.
+ */
+static int
+get_history_one(zpool_handle_t *zhp, void *data)
+{
+ nvlist_t *nvhis;
+ nvlist_t **records;
+ uint_t numrecords;
+ char *cmdstr;
+ uint64_t dst_time;
+ time_t tsec;
+ struct tm t;
+ char tbuf[30];
+ int ret, i;
+
+ *(boolean_t *)data = B_FALSE;
+
+ (void) printf(gettext("History for '%s':\n"), zpool_get_name(zhp));
+
+ if ((ret = zpool_get_history(zhp, &nvhis)) != 0)
+ return (ret);
+
+ verify(nvlist_lookup_nvlist_array(nvhis, ZPOOL_HIST_RECORD,
+ &records, &numrecords) == 0);
+ for (i = 0; i < numrecords; i++) {
+ if (nvlist_lookup_uint64(records[i], ZPOOL_HIST_TIME,
+ &dst_time) == 0) {
+ verify(nvlist_lookup_string(records[i], ZPOOL_HIST_CMD,
+ &cmdstr) == 0);
+ tsec = dst_time;
+ (void) localtime_r(&tsec, &t);
+ (void) strftime(tbuf, sizeof (tbuf), "%F.%T", &t);
+ (void) printf("%s %s\n", tbuf, cmdstr);
+ }
+ }
+ (void) printf("\n");
+ nvlist_free(nvhis);
+
+ return (ret);
+}
+
+/*
+ * zpool history <pool>
+ *
+ * Displays the history of commands that modified pools.
+ */
+int
+zpool_do_history(int argc, char **argv)
+{
+ boolean_t first = B_TRUE;
+ int ret;
+
+ argc -= optind;
+ argv += optind;
+
+ ret = for_each_pool(argc, argv, B_FALSE, NULL, get_history_one,
+ &first);
+
+ if (argc == 0 && first == B_TRUE) {
+ (void) printf(gettext("no pools available\n"));
+ return (0);
+ }
+
+ return (ret);
+}
+
+static int
+get_callback(zpool_handle_t *zhp, void *data)
+{
+ libzfs_get_cbdata_t *cbp = (libzfs_get_cbdata_t *)data;
+ char value[MAXNAMELEN];
+ zfs_source_t srctype;
+ zpool_proplist_t *pl;
+
+ for (pl = cbp->cb_proplist; pl != NULL; pl = pl->pl_next) {
+
+ /*
+ * Skip the special fake placeholder.
+ */
+ if (pl->pl_prop == ZFS_PROP_NAME &&
+ pl == cbp->cb_proplist)
+ continue;
+
+ if (zpool_get_prop(zhp, pl->pl_prop,
+ value, sizeof (value), &srctype) != 0)
+ continue;
+
+ libzfs_print_one_property(zpool_get_name(zhp), cbp,
+ zpool_prop_to_name(pl->pl_prop), value, srctype, NULL);
+ }
+ return (0);
+}
+
+int
+zpool_do_get(int argc, char **argv)
+{
+ libzfs_get_cbdata_t cb = { 0 };
+ zpool_proplist_t fake_name = { 0 };
+ int ret;
+
+ if (argc < 3)
+ usage(B_FALSE);
+
+ cb.cb_first = B_TRUE;
+ cb.cb_sources = ZFS_SRC_ALL;
+ cb.cb_columns[0] = GET_COL_NAME;
+ cb.cb_columns[1] = GET_COL_PROPERTY;
+ cb.cb_columns[2] = GET_COL_VALUE;
+ cb.cb_columns[3] = GET_COL_SOURCE;
+
+ if (zpool_get_proplist(g_zfs, argv[1], &cb.cb_proplist) != 0)
+ usage(B_FALSE);
+
+ if (cb.cb_proplist != NULL) {
+ fake_name.pl_prop = ZFS_PROP_NAME;
+ fake_name.pl_width = strlen(gettext("NAME"));
+ fake_name.pl_next = cb.cb_proplist;
+ cb.cb_proplist = &fake_name;
+ }
+
+ ret = for_each_pool(argc - 2, argv + 2, B_TRUE, &cb.cb_proplist,
+ get_callback, &cb);
+
+ if (cb.cb_proplist == &fake_name)
+ zfs_free_proplist(fake_name.pl_next);
+ else
+ zfs_free_proplist(cb.cb_proplist);
+
+ return (ret);
+}
+
+typedef struct set_cbdata {
+ char *cb_propname;
+ char *cb_value;
+ boolean_t cb_any_successful;
+} set_cbdata_t;
+
+int
+set_callback(zpool_handle_t *zhp, void *data)
+{
+ int error;
+ set_cbdata_t *cb = (set_cbdata_t *)data;
+
+ error = zpool_set_prop(zhp, cb->cb_propname, cb->cb_value);
+
+ if (!error)
+ cb->cb_any_successful = B_TRUE;
+
+ return (error);
+}
+
+int
+zpool_do_set(int argc, char **argv)
+{
+ set_cbdata_t cb = { 0 };
+ int error;
+
+ if (argc > 1 && argv[1][0] == '-') {
+ (void) fprintf(stderr, gettext("invalid option '%c'\n"),
+ argv[1][1]);
+ usage(B_FALSE);
+ }
+
+ if (argc < 2) {
+ (void) fprintf(stderr, gettext("missing property=value "
+ "argument\n"));
+ usage(B_FALSE);
+ }
+
+ if (argc < 3) {
+ (void) fprintf(stderr, gettext("missing pool name\n"));
+ usage(B_FALSE);
+ }
+
+ if (argc > 3) {
+ (void) fprintf(stderr, gettext("too many pool names\n"));
+ usage(B_FALSE);
+ }
+
+ cb.cb_propname = argv[1];
+ cb.cb_value = strchr(cb.cb_propname, '=');
+ if (cb.cb_value == NULL) {
+ (void) fprintf(stderr, gettext("missing value in "
+ "property=value argument\n"));
+ usage(B_FALSE);
+ }
+
+ *(cb.cb_value) = '\0';
+ cb.cb_value++;
+
+ error = for_each_pool(argc - 2, argv + 2, B_TRUE, NULL,
+ set_callback, &cb);
+
+ if (cb.cb_any_successful) {
+ *(cb.cb_value - 1) = '=';
+ zpool_log_history(g_zfs, argc, argv, argv[2], B_FALSE, B_FALSE);
+ }
+
+ return (error);
+}
+
+static int
+find_command_idx(char *command, int *idx)
+{
+ int i;
+
+ for (i = 0; i < NCOMMAND; i++) {
+ if (command_table[i].name == NULL)
+ continue;
+
+ if (strcmp(command, command_table[i].name) == 0) {
+ *idx = i;
+ return (0);
+ }
+ }
+ return (1);
+}
+
+int
+main(int argc, char **argv)
+{
+ int ret;
+ int i;
+ char *cmdname;
+ int found = 0;
+
+ (void) setlocale(LC_ALL, "");
+ (void) textdomain(TEXT_DOMAIN);
+
+ if ((g_zfs = libzfs_init()) == NULL) {
+ (void) fprintf(stderr, gettext("internal error: failed to "
+ "initialize ZFS library\n"));
+ return (1);
+ }
+
+ libzfs_print_on_error(g_zfs, B_TRUE);
+
+ opterr = 0;
+
+ /*
+ * Make sure the user has specified some command.
+ */
+ if (argc < 2) {
+ (void) fprintf(stderr, gettext("missing command\n"));
+ usage(B_FALSE);
+ }
+
+ cmdname = argv[1];
+
+ /*
+ * Special case '-?'
+ */
+ if (strcmp(cmdname, "-?") == 0)
+ usage(B_TRUE);
+
+ /*
+ * Run the appropriate command.
+ */
+ if (find_command_idx(cmdname, &i) == 0) {
+ current_command = &command_table[i];
+ ret = command_table[i].func(argc - 1, argv + 1);
+ found++;
+ }
+
+ /*
+ * 'freeze' is a vile debugging abomination, so we treat it as such.
+ */
+ if (strcmp(cmdname, "freeze") == 0 && argc == 3) {
+ char buf[16384];
+ int fd = open(ZFS_DEV, O_RDWR);
+ (void) strcpy((void *)buf, argv[2]);
+ return (!!ioctl(fd, ZFS_IOC_POOL_FREEZE, buf));
+ }
+
+ if (!found) {
+ (void) fprintf(stderr, gettext("unrecognized "
+ "command '%s'\n"), cmdname);
+ usage(B_FALSE);
+ }
+
+ libzfs_fini(g_zfs);
+
+ /*
+ * The 'ZFS_ABORT' environment variable causes us to dump core on exit
+ * for the purposes of running ::findleaks.
+ */
+ if (getenv("ZFS_ABORT") != NULL) {
+ (void) printf("dumping core by request\n");
+ abort();
+ }
+
+ return (ret);
+}
OpenPOWER on IntegriCloud