diff options
author | jb <jb@FreeBSD.org> | 2008-03-28 00:08:47 +0000 |
---|---|---|
committer | jb <jb@FreeBSD.org> | 2008-03-28 00:08:47 +0000 |
commit | 291b24b7551a7f0ab80c6cc152155e317f53be0a (patch) | |
tree | 9edd0223cc16fd15bec321b8d36de00f35487d3b /contrib/opensolaris/cmd/zpool/zpool_main.c | |
parent | 5794ada908cc2e195ebcb0217b7164e3bd207e1b (diff) | |
download | FreeBSD-src-291b24b7551a7f0ab80c6cc152155e317f53be0a.zip FreeBSD-src-291b24b7551a7f0ab80c6cc152155e317f53be0a.tar.gz |
Remove files that have been repo copied to their new location
in cddl-specific parts of the source tree.
Diffstat (limited to 'contrib/opensolaris/cmd/zpool/zpool_main.c')
-rw-r--r-- | contrib/opensolaris/cmd/zpool/zpool_main.c | 3602 |
1 files changed, 0 insertions, 3602 deletions
diff --git a/contrib/opensolaris/cmd/zpool/zpool_main.c b/contrib/opensolaris/cmd/zpool/zpool_main.c deleted file mode 100644 index 5b1d856..0000000 --- a/contrib/opensolaris/cmd/zpool/zpool_main.c +++ /dev/null @@ -1,3602 +0,0 @@ -/* - * 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; - - case ZPOOL_STATUS_HOSTID_MISMATCH: - (void) printf(gettext("status: The pool was last accessed by " - "another system.\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 if (reason == ZPOOL_STATUS_HOSTID_MISMATCH) - (void) printf(gettext("action: The pool can be " - "imported using its name or numeric " - "identifier and\n\tthe '-f' flag.\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) { - uint64_t hostid; - - if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_HOSTID, - &hostid) == 0) { - if ((unsigned long)hostid != gethostid()) { - char *hostname; - uint64_t timestamp; - time_t t; - - verify(nvlist_lookup_string(config, - ZPOOL_CONFIG_HOSTNAME, &hostname) == 0); - verify(nvlist_lookup_uint64(config, - ZPOOL_CONFIG_TIMESTAMP, ×tamp) == 0); - t = timestamp; - (void) fprintf(stderr, gettext("cannot import " - "'%s': pool may be in use from other " - "system, it was last accessed by %s " - "(hostid: 0x%lx) on %s"), name, hostname, - (unsigned long)hostid, - asctime(localtime(&t))); - (void) fprintf(stderr, gettext("use '-f' to " - "import anyway\n")); - return (1); - } - } else { - (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, - ¬present) == 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); -} |