diff options
author | pjd <pjd@FreeBSD.org> | 2008-11-17 20:49:29 +0000 |
---|---|---|
committer | pjd <pjd@FreeBSD.org> | 2008-11-17 20:49:29 +0000 |
commit | bbe899b96e388a8b82439f81ed3707e0d9c6070d (patch) | |
tree | 81b89fa4ac6467771d5aa291a97f4665981a6108 /cddl/contrib/opensolaris/cmd/zpool/zpool_main.c | |
parent | d2f579595c362ce27b4d87e2c40e1c4e09b929e3 (diff) | |
download | FreeBSD-src-bbe899b96e388a8b82439f81ed3707e0d9c6070d.zip FreeBSD-src-bbe899b96e388a8b82439f81ed3707e0d9c6070d.tar.gz |
Update ZFS from version 6 to 13 and bring some FreeBSD-specific changes.
This bring huge amount of changes, I'll enumerate only user-visible changes:
- Delegated Administration
Allows regular users to perform ZFS operations, like file system
creation, snapshot creation, etc.
- L2ARC
Level 2 cache for ZFS - allows to use additional disks for cache.
Huge performance improvements mostly for random read of mostly
static content.
- slog
Allow to use additional disks for ZFS Intent Log to speed up
operations like fsync(2).
- vfs.zfs.super_owner
Allows regular users to perform privileged operations on files stored
on ZFS file systems owned by him. Very careful with this one.
- chflags(2)
Not all the flags are supported. This still needs work.
- ZFSBoot
Support to boot off of ZFS pool. Not finished, AFAIK.
Submitted by: dfr
- Snapshot properties
- New failure modes
Before if write requested failed, system paniced. Now one
can select from one of three failure modes:
- panic - panic on write error
- wait - wait for disk to reappear
- continue - serve read requests if possible, block write requests
- Refquota, refreservation properties
Just quota and reservation properties, but don't count space consumed
by children file systems, clones and snapshots.
- Sparse volumes
ZVOLs that don't reserve space in the pool.
- External attributes
Compatible with extattr(2).
- NFSv4-ACLs
Not sure about the status, might not be complete yet.
Submitted by: trasz
- Creation-time properties
- Regression tests for zpool(8) command.
Obtained from: OpenSolaris
Diffstat (limited to 'cddl/contrib/opensolaris/cmd/zpool/zpool_main.c')
-rw-r--r-- | cddl/contrib/opensolaris/cmd/zpool/zpool_main.c | 1338 |
1 files changed, 843 insertions, 495 deletions
diff --git a/cddl/contrib/opensolaris/cmd/zpool/zpool_main.c b/cddl/contrib/opensolaris/cmd/zpool/zpool_main.c index 5b1d856..2388df9 100644 --- a/cddl/contrib/opensolaris/cmd/zpool/zpool_main.c +++ b/cddl/contrib/opensolaris/cmd/zpool/zpool_main.c @@ -20,12 +20,10 @@ */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 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> @@ -42,6 +40,8 @@ #include <strings.h> #include <unistd.h> #include <priv.h> +#include <pwd.h> +#include <zone.h> #include <sys/time.h> #include <sys/fs/zfs.h> @@ -50,6 +50,7 @@ #include <libzfs.h> #include "zpool_util.h" +#include "zfs_comutil.h" static int zpool_do_create(int, char **); static int zpool_do_destroy(int, char **); @@ -85,6 +86,8 @@ static int zpool_do_set(int, char **); * These libumem hooks provide a reasonable set of defaults for the allocator's * debugging facilities. */ + +#ifdef DEBUG const char * _umem_debug_init(void) { @@ -96,6 +99,7 @@ _umem_logging_init(void) { return ("fail,contents"); /* $UMEM_LOGGING setting */ } +#endif typedef enum { HELP_ADD, @@ -169,6 +173,7 @@ static zpool_command_t command_table[] = { #define NCOMMAND (sizeof (command_table) / sizeof (command_table[0])) zpool_command_t *current_command; +static char history_str[HIS_MAX_RECORD_LEN]; static const char * get_usage(zpool_help_t idx) { @@ -177,12 +182,13 @@ get_usage(zpool_help_t idx) { return (gettext("\tadd [-fn] <pool> <vdev> ...\n")); case HELP_ATTACH: return (gettext("\tattach [-f] <pool> <device> " - "<new_device>\n")); + "<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")); + return (gettext("\tcreate [-fn] [-o property=value] ... \n" + "\t [-O file-system-property=value] ... \n" + "\t [-m mountpoint] [-R root] <pool> <vdev> ...\n")); case HELP_DESTROY: return (gettext("\tdestroy [-f] <pool>\n")); case HELP_DETACH: @@ -190,17 +196,19 @@ get_usage(zpool_help_t idx) { case HELP_EXPORT: return (gettext("\texport [-f] <pool> ...\n")); case HELP_HISTORY: - return (gettext("\thistory [<pool>]\n")); + return (gettext("\thistory [-il] [<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")); + "\timport [-o mntopts] [-o property=value] ... \n" + "\t [-d dir | -c cachefile] [-D] [-f] [-R root] -a\n" + "\timport [-o mntopts] [-o property=value] ... \n" + "\t [-d dir | -c cachefile] [-D] [-f] [-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]*] " + return (gettext("\tlist [-H] [-o property[,...]] " "[pool] ...\n")); case HELP_OFFLINE: return (gettext("\toffline [-t] <pool> <device> ...\n")); @@ -208,9 +216,9 @@ get_usage(zpool_help_t idx) { return (gettext("\tonline <pool> <device> ...\n")); case HELP_REPLACE: return (gettext("\treplace [-f] <pool> <device> " - "[new_device]\n")); + "[new-device]\n")); case HELP_REMOVE: - return (gettext("\tremove <pool> <device>\n")); + return (gettext("\tremove <pool> <device> ...\n")); case HELP_SCRUB: return (gettext("\tscrub [-s] <pool> ...\n")); case HELP_STATUS: @@ -218,9 +226,9 @@ get_usage(zpool_help_t idx) { case HELP_UPGRADE: return (gettext("\tupgrade\n" "\tupgrade -v\n" - "\tupgrade <-a | pool>\n")); + "\tupgrade [-V version] <-a | pool ...>\n")); case HELP_GET: - return (gettext("\tget <all | property[,property]...> " + return (gettext("\tget <\"all\" | property[,...]> " "<pool> ...\n")); case HELP_SET: return (gettext("\tset <property=value> <pool> \n")); @@ -230,67 +238,28 @@ get_usage(zpool_help_t idx) { /* 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) +static int +print_prop_cb(int prop, void *cb) { FILE *fp = cb; (void) fprintf(fp, "\t%-13s ", zpool_prop_to_name(prop)); + if (zpool_prop_readonly(prop)) + (void) fprintf(fp, " NO "); + else + (void) fprintf(fp, " YES "); + if (zpool_prop_values(prop) == NULL) (void) fprintf(fp, "-\n"); else (void) fprintf(fp, "%s\n", zpool_prop_values(prop)); - return (ZFS_PROP_CONT); + return (ZPROP_CONT); } /* @@ -301,7 +270,6 @@ print_prop_cb(zpool_prop_t prop, void *cb) void usage(boolean_t requested) { - int i; FILE *fp = requested ? stdout : stderr; if (current_command == NULL) { @@ -321,28 +289,22 @@ usage(boolean_t requested) } 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))) { + (strcmp(current_command->name, "get") == 0) || + (strcmp(current_command->name, "list") == 0))) { (void) fprintf(fp, gettext("\nthe following properties are supported:\n")); - (void) fprintf(fp, "\n\t%-13s %s\n\n", - "PROPERTY", "VALUES"); + (void) fprintf(fp, "\n\t%-13s %s %s\n\n", + "PROPERTY", "EDIT", "VALUES"); /* Iterate over all properties */ - (void) zpool_prop_iter(print_prop_cb, fp, B_FALSE); + (void) zprop_iter(print_prop_cb, fp, B_FALSE, B_TRUE, + ZFS_TYPE_POOL); } /* @@ -356,46 +318,9 @@ usage(boolean_t requested) 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) +print_vdev_tree(zpool_handle_t *zhp, const char *name, nvlist_t *nv, int indent, + boolean_t print_logs) { nvlist_t **child; uint_t c, children; @@ -409,13 +334,75 @@ print_vdev_tree(zpool_handle_t *zhp, const char *name, nvlist_t *nv, int indent) return; for (c = 0; c < children; c++) { + uint64_t is_log = B_FALSE; + + (void) nvlist_lookup_uint64(child[c], ZPOOL_CONFIG_IS_LOG, + &is_log); + if ((is_log && !print_logs) || (!is_log && print_logs)) + continue; + vname = zpool_vdev_name(g_zfs, zhp, child[c]); - print_vdev_tree(zhp, vname, child[c], indent + 2); + print_vdev_tree(zhp, vname, child[c], indent + 2, + B_FALSE); free(vname); } } /* + * Add a property pair (name, string-value) into a property nvlist. + */ +static int +add_prop_list(const char *propname, char *propval, nvlist_t **props, + boolean_t poolprop) +{ + zpool_prop_t prop = ZPROP_INVAL; + zfs_prop_t fprop; + nvlist_t *proplist; + const char *normnm; + char *strval; + + if (*props == NULL && + nvlist_alloc(props, NV_UNIQUE_NAME, 0) != 0) { + (void) fprintf(stderr, + gettext("internal error: out of memory\n")); + return (1); + } + + proplist = *props; + + if (poolprop) { + if ((prop = zpool_name_to_prop(propname)) == ZPROP_INVAL) { + (void) fprintf(stderr, gettext("property '%s' is " + "not a valid pool property\n"), propname); + return (2); + } + normnm = zpool_prop_to_name(prop); + } else { + if ((fprop = zfs_name_to_prop(propname)) == ZPROP_INVAL) { + (void) fprintf(stderr, gettext("property '%s' is " + "not a valid file system property\n"), propname); + return (2); + } + normnm = zfs_prop_to_name(fprop); + } + + if (nvlist_lookup_string(proplist, normnm, &strval) == 0 && + prop != ZPOOL_PROP_CACHEFILE) { + (void) fprintf(stderr, gettext("property '%s' " + "specified multiple times\n"), propname); + return (2); + } + + if (nvlist_add_string(proplist, normnm, propval) != 0) { + (void) fprintf(stderr, gettext("internal " + "error: out of memory\n")); + return (1); + } + + return (0); +} + +/* * zpool add [-fn] <pool> <vdev> ... * * -f Force addition of devices, even if they appear in use @@ -483,7 +470,8 @@ zpool_do_add(int argc, char **argv) } /* pass off to get_vdev_spec for processing */ - nvroot = make_root_vdev(config, force, !force, B_FALSE, argc, argv); + nvroot = make_root_vdev(zhp, force, !force, B_FALSE, dryrun, + argc, argv); if (nvroot == NULL) { zpool_close(zhp); return (1); @@ -498,16 +486,21 @@ zpool_do_add(int argc, char **argv) (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); + /* print original main pool and new tree */ + print_vdev_tree(zhp, poolname, poolnvroot, 0, B_FALSE); + print_vdev_tree(zhp, NULL, nvroot, 0, B_FALSE); + + /* Do the same for the logs */ + if (num_logs(poolnvroot) > 0) { + print_vdev_tree(zhp, "logs", poolnvroot, 0, B_TRUE); + print_vdev_tree(zhp, NULL, nvroot, 0, B_TRUE); + } else if (num_logs(nvroot) > 0) { + print_vdev_tree(zhp, "logs", nvroot, 0, B_TRUE); + } 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); @@ -517,17 +510,17 @@ zpool_do_add(int argc, char **argv) } /* - * zpool remove <pool> <vdev> + * 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. + * spares and cache devices 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; + int i, ret = 0; zpool_handle_t *zhp; argc--; @@ -548,17 +541,18 @@ zpool_do_remove(int argc, char **argv) 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); + for (i = 1; i < argc; i++) { + if (zpool_vdev_remove(zhp, argv[i]) != 0) + ret = 1; } return (ret); } /* - * zpool create [-fn] [-R root] [-m mountpoint] <pool> <dev> ... + * zpool create [-fn] [-o property=value] ... + * [-O file-system-property=value] ... + * [-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 @@ -566,6 +560,8 @@ zpool_do_remove(int argc, char **argv) * -R Create a pool under an alternate root * -m Set default mountpoint for the root dataset. By default it's * '/<pool>' + * -o Set property=value. + * -O Set fsproperty=value in the pool's root file system * * 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 @@ -578,16 +574,17 @@ zpool_do_create(int argc, char **argv) boolean_t force = B_FALSE; boolean_t dryrun = B_FALSE; int c; - nvlist_t *nvroot; + nvlist_t *nvroot = NULL; char *poolname; - int ret; + int ret = 1; char *altroot = NULL; char *mountpoint = NULL; - nvlist_t **child; - uint_t children; + nvlist_t *fsprops = NULL; + nvlist_t *props = NULL; + char *propval; /* check options */ - while ((c = getopt(argc, argv, ":fnR:m:")) != -1) { + while ((c = getopt(argc, argv, ":fnR:m:o:O:")) != -1) { switch (c) { case 'f': force = B_TRUE; @@ -597,19 +594,52 @@ zpool_do_create(int argc, char **argv) break; case 'R': altroot = optarg; + if (add_prop_list(zpool_prop_to_name( + ZPOOL_PROP_ALTROOT), optarg, &props, B_TRUE)) + goto errout; + if (nvlist_lookup_string(props, + zpool_prop_to_name(ZPOOL_PROP_CACHEFILE), + &propval) == 0) + break; + if (add_prop_list(zpool_prop_to_name( + ZPOOL_PROP_CACHEFILE), "none", &props, B_TRUE)) + goto errout; break; case 'm': mountpoint = optarg; break; + case 'o': + if ((propval = strchr(optarg, '=')) == NULL) { + (void) fprintf(stderr, gettext("missing " + "'=' for -o option\n")); + goto errout; + } + *propval = '\0'; + propval++; + + if (add_prop_list(optarg, propval, &props, B_TRUE)) + goto errout; + break; + case 'O': + if ((propval = strchr(optarg, '=')) == NULL) { + (void) fprintf(stderr, gettext("missing " + "'=' for -O option\n")); + goto errout; + } + *propval = '\0'; + propval++; + + if (add_prop_list(optarg, propval, &fsprops, B_FALSE)) + goto errout; + break; case ':': (void) fprintf(stderr, gettext("missing argument for " "'%c' option\n"), optopt); - usage(B_FALSE); - break; + goto badusage; case '?': (void) fprintf(stderr, gettext("invalid option '%c'\n"), optopt); - usage(B_FALSE); + goto badusage; } } @@ -619,11 +649,11 @@ zpool_do_create(int argc, char **argv) /* get pool name and check number of arguments */ if (argc < 1) { (void) fprintf(stderr, gettext("missing pool name argument\n")); - usage(B_FALSE); + goto badusage; } if (argc < 2) { (void) fprintf(stderr, gettext("missing vdev specification\n")); - usage(B_FALSE); + goto badusage; } poolname = argv[0]; @@ -637,31 +667,28 @@ zpool_do_create(int argc, char **argv) "character '/' in pool name\n"), poolname); (void) fprintf(stderr, gettext("use 'zfs create' to " "create a dataset\n")); - return (1); + goto errout; } /* pass off to get_vdev_spec for bulk processing */ - nvroot = make_root_vdev(NULL, force, !force, B_FALSE, argc - 1, - argv + 1); + nvroot = make_root_vdev(NULL, force, !force, B_FALSE, dryrun, + argc - 1, argv + 1); if (nvroot == NULL) - return (1); + goto errout; /* 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) { + if (!zfs_allocatable_devs(nvroot)) { (void) fprintf(stderr, gettext("invalid vdev " "specification: at least one toplevel vdev must be " "specified\n")); - return (1); + goto errout; } 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); + goto errout; } /* @@ -672,14 +699,13 @@ zpool_do_create(int argc, char **argv) (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) != 0 && strcmp(mountpoint, ZFS_MOUNTPOINT_NONE) != 0)) { char buf[MAXPATHLEN]; - struct stat64 statbuf; + DIR *dirp; 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); + goto errout; } if (mountpoint == NULL) { @@ -698,23 +724,30 @@ zpool_do_create(int argc, char **argv) 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); + if ((dirp = opendir(buf)) == NULL && errno != ENOENT) { + (void) fprintf(stderr, gettext("mountpoint '%s' : " + "%s\n"), buf, strerror(errno)); (void) fprintf(stderr, gettext("use '-m' " "option to provide a different default\n")); - nvlist_free(nvroot); - return (1); + goto errout; + } else if (dirp) { + int count = 0; + + while (count < 3 && readdir(dirp) != NULL) + count++; + (void) closedir(dirp); + + if (count > 2) { + (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")); + goto errout; + } } } - if (dryrun) { /* * For a dry run invocation, print out a basic message and run @@ -724,15 +757,17 @@ zpool_do_create(int argc, char **argv) (void) printf(gettext("would create '%s' with the " "following layout:\n\n"), poolname); - print_vdev_tree(NULL, poolname, nvroot, 0); + print_vdev_tree(NULL, poolname, nvroot, 0, B_FALSE); + if (num_logs(nvroot) > 0) + print_vdev_tree(NULL, "logs", nvroot, 0, B_TRUE); ret = 0; } else { - ret = 1; /* * Hand off to libzfs. */ - if (zpool_create(g_zfs, poolname, nvroot, altroot) == 0) { + if (zpool_create(g_zfs, poolname, + nvroot, props, fsprops) == 0) { zfs_handle_t *pool = zfs_open(g_zfs, poolname, ZFS_TYPE_FILESYSTEM); if (pool != NULL) { @@ -742,20 +777,25 @@ zpool_do_create(int argc, char **argv) ZFS_PROP_MOUNTPOINT), mountpoint) == 0); if (zfs_mount(pool, NULL, 0) == 0) - ret = zfs_share_nfs(pool); + ret = zfs_shareall(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")); } } +errout: nvlist_free(nvroot); - + nvlist_free(fsprops); + nvlist_free(props); return (ret); +badusage: + nvlist_free(fsprops); + nvlist_free(props); + usage(B_FALSE); + return (2); } /* @@ -819,9 +859,6 @@ zpool_do_destroy(int argc, char **argv) return (1); } - zpool_log_history(g_zfs, argc + optind, argv - optind, pool, B_TRUE, - B_FALSE); - ret = (zpool_destroy(zhp) != 0); zpool_close(zhp); @@ -882,10 +919,7 @@ zpool_do_export(int argc, char **argv) continue; } - zpool_log_history(g_zfs, argc + optind, argv - optind, argv[i], - B_TRUE, B_FALSE); - - if (zpool_export(zhp) != 0) + if (zpool_export(zhp, force) != 0) ret = 1; zpool_close(zhp); @@ -919,6 +953,14 @@ max_width(zpool_handle_t *zhp, nvlist_t *nv, int depth, int max) max = ret; } + if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_L2CACHE, + &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++) @@ -937,7 +979,8 @@ max_width(zpool_handle_t *zhp, nvlist_t *nv, int depth, int max) * pool, printing out the name and status for each one. */ void -print_import_config(const char *name, nvlist_t *nv, int namewidth, int depth) +print_import_config(const char *name, nvlist_t *nv, int namewidth, int depth, + boolean_t print_logs) { nvlist_t **child; uint_t c, children; @@ -952,9 +995,10 @@ print_import_config(const char *name, nvlist_t *nv, int namewidth, int depth) (uint64_t **)&vs, &c) == 0); (void) printf("\t%*s%-*s", depth, "", namewidth - depth, name); + (void) printf(" %s", zpool_state_to_name(vs->vs_state, vs->vs_aux)); if (vs->vs_aux != 0) { - (void) printf(" %-8s ", state_to_name(vs)); + (void) printf(" "); switch (vs->vs_aux) { case VDEV_AUX_OPEN_FAILED: @@ -973,12 +1017,14 @@ print_import_config(const char *name, nvlist_t *nv, int namewidth, int depth) (void) printf(gettext("newer version")); break; + case VDEV_AUX_ERR_EXCEEDED: + (void) printf(gettext("too many errors")); + break; + default: (void) printf(gettext("corrupted data")); break; } - } else { - (void) printf(" %s", state_to_name(vs)); } (void) printf("\n"); @@ -987,21 +1033,37 @@ print_import_config(const char *name, nvlist_t *nv, int namewidth, int depth) return; for (c = 0; c < children; c++) { + uint64_t is_log = B_FALSE; + + (void) nvlist_lookup_uint64(child[c], ZPOOL_CONFIG_IS_LOG, + &is_log); + if ((is_log && !print_logs) || (!is_log && print_logs)) + continue; + vname = zpool_vdev_name(g_zfs, NULL, child[c]); print_import_config(vname, child[c], - namewidth, depth + 2); + namewidth, depth + 2, B_FALSE); free(vname); } - if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_SPARES, - &child, &children) != 0) - return; + if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_L2CACHE, + &child, &children) == 0) { + (void) printf(gettext("\tcache\n")); + for (c = 0; c < children; c++) { + vname = zpool_vdev_name(g_zfs, NULL, child[c]); + (void) printf("\t %s\n", vname); + free(vname); + } + } - (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); + if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_SPARES, + &child, &children) == 0) { + (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); + } } } @@ -1033,7 +1095,7 @@ show_import(nvlist_t *config) verify(nvlist_lookup_uint64_array(nvroot, ZPOOL_CONFIG_STATS, (uint64_t **)&vs, &vsc) == 0); - health = state_to_health(vs->vs_state); + health = zpool_state_to_name(vs->vs_state, vs->vs_aux); reason = zpool_import_status(config, &msgid); @@ -1086,6 +1148,18 @@ show_import(nvlist_t *config) (void) printf(gettext("status: The pool was last accessed by " "another system.\n")); break; + + case ZPOOL_STATUS_FAULTED_DEV_R: + case ZPOOL_STATUS_FAULTED_DEV_NR: + (void) printf(gettext("status: One or more devices are " + "faulted.\n")); + break; + + case ZPOOL_STATUS_BAD_LOG: + (void) printf(gettext("status: An intent log record cannot be " + "read.\n")); + break; + default: /* * No other status can be seen when importing pools. @@ -1147,7 +1221,7 @@ show_import(nvlist_t *config) "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" + "another system, but can be imported using\n\t" "the '-f' flag.\n")); } @@ -1160,7 +1234,12 @@ show_import(nvlist_t *config) namewidth = max_width(NULL, nvroot, 0, 0); if (namewidth < 10) namewidth = 10; - print_import_config(name, nvroot, namewidth, 0); + + print_import_config(name, nvroot, namewidth, 0, B_FALSE); + if (num_logs(nvroot) > 0) { + (void) printf(gettext("\tlogs\n")); + print_import_config(name, nvroot, namewidth, 0, B_TRUE); + } if (reason == ZPOOL_STATUS_BAD_GUID_SUM) { (void) printf(gettext("\n\tAdditional devices are known to " @@ -1171,17 +1250,18 @@ show_import(nvlist_t *config) /* * 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. + * lifting off to zpool_import_props(), 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) + int force, nvlist_t *props, boolean_t allowfaulted) { zpool_handle_t *zhp; char *name; uint64_t state; uint64_t version; + int error = 0; verify(nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME, &name) == 0); @@ -1190,7 +1270,7 @@ do_import(nvlist_t *config, const char *newname, const char *mntopts, ZPOOL_CONFIG_POOL_STATE, &state) == 0); verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_VERSION, &version) == 0); - if (version > ZFS_VERSION) { + if (version > SPA_VERSION) { (void) fprintf(stderr, gettext("cannot import '%s': pool " "is formatted using a newer ZFS version\n"), name); return (1); @@ -1228,15 +1308,14 @@ do_import(nvlist_t *config, const char *newname, const char *mntopts, } } - if (zpool_import(g_zfs, config, newname, altroot) != 0) + if (zpool_import_props(g_zfs, config, newname, props, + allowfaulted) != 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); + verify((zhp = zpool_open_canfail(g_zfs, name)) != NULL); if (zpool_enable_datasets(zhp, mntopts, 0) != 0) { zpool_close(zhp); @@ -1244,13 +1323,18 @@ do_import(nvlist_t *config, const char *newname, const char *mntopts, } zpool_close(zhp); - return (0); + return (error); } /* * zpool import [-d dir] [-D] - * import [-R root] [-D] [-d dir] [-f] -a - * import [-R root] [-D] [-d dir] [-f] <pool | id> [newpool] + * import [-o mntopts] [-o prop=value] ... [-R root] [-D] + * [-d dir | -c cachefile] [-f] -a + * import [-o mntopts] [-o prop=value] ... [-R root] [-D] + * [-d dir | -c cachefile] [-f] <pool | id> [newpool] + * + * -c Read pool information from a cachefile instead of searching + * devices. * * -d Scan in a specific directory, other than /dev/dsk. More than * one directory can be specified using multiple '-d' options. @@ -1264,8 +1348,15 @@ do_import(nvlist_t *config, const char *newname, const char *mntopts, * * -f Force import, even if it appears that the pool is active. * + * -F Import even in the presence of faulted vdevs. This is an + * intentionally undocumented option for testing purposes, and + * treats the pool configuration as complete, leaving any bad + * vdevs in the FAULTED state. + * * -a Import all pools found. * + * -o Set property=value and/or temporary mount options (without '='). + * * 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. */ @@ -1276,26 +1367,32 @@ zpool_do_import(int argc, char **argv) int nsearch = 0; int c; int err; - nvlist_t *pools; + nvlist_t *pools = NULL; 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; + uint64_t searchguid = 0; + char *searchname = NULL; + char *propval; nvlist_t *found_config; + nvlist_t *props = NULL; boolean_t first; + boolean_t allow_faulted = B_FALSE; uint64_t pool_state; + char *cachefile = NULL; /* check options */ - while ((c = getopt(argc, argv, ":Dfd:R:ao:")) != -1) { + while ((c = getopt(argc, argv, ":ac:d:DfFo:p:R:")) != -1) { switch (c) { case 'a': do_all = B_TRUE; break; + case 'c': + cachefile = optarg; + break; case 'd': if (searchdirs == NULL) { searchdirs = safe_malloc(sizeof (char *)); @@ -1315,11 +1412,31 @@ zpool_do_import(int argc, char **argv) case 'f': do_force = B_TRUE; break; + case 'F': + allow_faulted = B_TRUE; + break; case 'o': - mntopts = optarg; + if ((propval = strchr(optarg, '=')) != NULL) { + *propval = '\0'; + propval++; + if (add_prop_list(optarg, propval, + &props, B_TRUE)) + goto error; + } else { + mntopts = optarg; + } break; case 'R': - altroot = optarg; + if (add_prop_list(zpool_prop_to_name( + ZPOOL_PROP_ALTROOT), optarg, &props, B_TRUE)) + goto error; + if (nvlist_lookup_string(props, + zpool_prop_to_name(ZPOOL_PROP_CACHEFILE), + &propval) == 0) + break; + if (add_prop_list(zpool_prop_to_name( + ZPOOL_PROP_CACHEFILE), "none", &props, B_TRUE)) + goto error; break; case ':': (void) fprintf(stderr, gettext("missing argument for " @@ -1336,9 +1453,14 @@ zpool_do_import(int argc, char **argv) argc -= optind; argv += optind; + if (cachefile && nsearch != 0) { + (void) fprintf(stderr, gettext("-c is incompatible with -d\n")); + usage(B_FALSE); + } + if (searchdirs == NULL) { searchdirs = safe_malloc(sizeof (char *)); - searchdirs[0] = "/dev"; + searchdirs[0] = "/dev/dsk"; nsearch = 1; } @@ -1367,13 +1489,7 @@ zpool_do_import(int argc, char **argv) } } - 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 @@ -1393,11 +1509,38 @@ zpool_do_import(int argc, char **argv) searchguid = strtoull(argv[0], &endptr, 10); if (errno != 0 || *endptr != '\0') searchname = argv[0]; - else - searchname = NULL; found_config = NULL; } + if (cachefile) { + pools = zpool_find_import_cached(g_zfs, cachefile, searchname, + searchguid); + } else if (searchname != NULL) { + pools = zpool_find_import_byname(g_zfs, nsearch, searchdirs, + searchname); + } else { + /* + * It's OK to search by guid even if searchguid is 0. + */ + pools = zpool_find_import_byguid(g_zfs, nsearch, searchdirs, + searchguid); + } + + if (pools == NULL) { + if (argc != 0) { + (void) fprintf(stderr, gettext("cannot import '%s': " + "no such pool available\n"), argv[0]); + } + free(searchdirs); + return (1); + } + + /* + * At this point we have a list of import candidate configs. Even if + * we were searching by pool name or guid, we still need to + * post-process the list to deal with pool state and possible + * duplicate names. + */ err = 0; elem = NULL; first = B_TRUE; @@ -1420,8 +1563,7 @@ zpool_do_import(int argc, char **argv) if (do_all) err |= do_import(config, NULL, mntopts, - altroot, do_force, argc + optind, - argv - optind); + do_force, props, allow_faulted); else show_import(config); } else if (searchname != NULL) { @@ -1469,8 +1611,7 @@ zpool_do_import(int argc, char **argv) err = B_TRUE; } else { err |= do_import(found_config, argc == 1 ? NULL : - argv[1], mntopts, altroot, do_force, argc + optind, - argv - optind); + argv[1], mntopts, do_force, props, allow_faulted); } } @@ -1482,6 +1623,8 @@ zpool_do_import(int argc, char **argv) (void) fprintf(stderr, gettext("no pools available to import\n")); +error: + nvlist_free(props); nvlist_free(pools); free(searchdirs); @@ -1518,7 +1661,7 @@ print_iostat_header(iostat_cbdata_t *cb) /* * Display a single statistic. */ -void +static void print_one_stat(uint64_t value) { char buf[64]; @@ -1606,6 +1749,28 @@ print_vdev_stats(zpool_handle_t *zhp, const char *name, nvlist_t *oldnv, newchild[c], cb, depth + 2); free(vname); } + + /* + * Include level 2 ARC devices in iostat output + */ + if (nvlist_lookup_nvlist_array(newnv, ZPOOL_CONFIG_L2CACHE, + &newchild, &children) != 0) + return; + + if (oldnv && nvlist_lookup_nvlist_array(oldnv, ZPOOL_CONFIG_L2CACHE, + &oldchild, &c) != 0) + return; + + if (children > 0) { + (void) printf("%-*s - - - - - " + "-\n", cb->cb_namewidth, "cache"); + 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 @@ -1870,160 +2035,122 @@ zpool_do_iostat(int argc, char **argv) typedef struct list_cbdata { boolean_t cb_scripted; boolean_t cb_first; - int cb_fields[MAX_FIELDS]; - int cb_fieldcount; + zprop_list_t *cb_proplist; } list_cbdata_t; /* * Given a list of columns to display, output appropriate headers for each one. */ -void -print_header(int *fields, size_t count) +static void +print_header(zprop_list_t *pl) { - int i; - column_def_t *col; - const char *fmt; + const char *header; + boolean_t first = B_TRUE; + boolean_t right_justify; - for (i = 0; i < count; i++) { - col = &column_table[fields[i]]; - if (i != 0) + for (; pl != NULL; pl = pl->pl_next) { + if (pl->pl_prop == ZPROP_INVAL) + continue; + + if (!first) (void) printf(" "); - if (col->cd_justify == left_justify) - fmt = "%-*s"; else - fmt = "%*s"; + first = B_FALSE; + + header = zpool_prop_column_name(pl->pl_prop); + right_justify = zpool_prop_align_right(pl->pl_prop); - (void) printf(fmt, i == count - 1 ? strlen(col->cd_title) : - col->cd_width, col->cd_title); + if (pl->pl_next == NULL && !right_justify) + (void) printf("%s", header); + else if (right_justify) + (void) printf("%*s", pl->pl_width, header); + else + (void) printf("%-*s", pl->pl_width, header); } (void) printf("\n"); } -int -list_callback(zpool_handle_t *zhp, void *data) +/* + * Given a pool and a list of properties, print out all the properties according + * to the described layout. + */ +static void +print_pool(zpool_handle_t *zhp, zprop_list_t *pl, int scripted) { - 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) + boolean_t first = B_TRUE; + char property[ZPOOL_MAXPROPLEN]; + char *propstr; + boolean_t right_justify; + int width; + + for (; pl != NULL; pl = pl->pl_next) { + if (!first) { + if (scripted) (void) printf("\t"); else (void) printf(" "); + } else { + first = B_FALSE; } - 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)); + right_justify = B_FALSE; + if (pl->pl_prop != ZPROP_INVAL) { + if (zpool_get_prop(zhp, pl->pl_prop, property, + sizeof (property), NULL) != 0) + propstr = "-"; else - zfs_nicenum(used, buf, sizeof (buf)); - break; + propstr = property; - case ZPOOL_FIELD_AVAILABLE: - if (config == NULL) - (void) strlcpy(buf, "-", sizeof (buf)); - else - zfs_nicenum(total - used, buf, sizeof (buf)); - break; + right_justify = zpool_prop_align_right(pl->pl_prop); + } else { + propstr = "-"; + } - 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; + width = pl->pl_width; - 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; + /* + * If this is being called in scripted mode, or if this is the + * last column and it is left-justified, don't include a width + * format specifier. + */ + if (scripted || (pl->pl_next == NULL && !right_justify)) + (void) printf("%s", propstr); + else if (right_justify) + (void) printf("%*s", width, propstr); + else + (void) printf("%-*s", width, propstr); + } - 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; - } + (void) printf("\n"); +} - if (cbp->cb_scripted) - (void) printf("%s", buf); - else { - if (col->cd_justify == left_justify) - fmt = "%-*s"; - else - fmt = "%*s"; +/* + * Generic callback function to list a pool. + */ +int +list_callback(zpool_handle_t *zhp, void *data) +{ + list_cbdata_t *cbp = data; - (void) printf(fmt, i == cbp->cb_fieldcount - 1 ? - strlen(buf) : col->cd_width, buf); - } + if (cbp->cb_first) { + if (!cbp->cb_scripted) + print_header(cbp->cb_proplist); + cbp->cb_first = B_FALSE; } - (void) printf("\n"); + print_pool(zhp, cbp->cb_proplist, cbp->cb_scripted); return (0); } /* - * zpool list [-H] [-o field[,field]*] [pool] ... + * zpool list [-H] [-o prop[,prop]*] [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" + * -H Scripted mode. Don't display headers, and separate properties + * by a single tab. + * -o List of properties to display. Defaults to + * "name,size,used,available,capacity,health,altroot" * * List all pools in the system, whether or not they're healthy. Output space * statistics for each one, as well as health status summary. @@ -2034,10 +2161,9 @@ 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; + static char default_props[] = + "name,size,used,available,capacity,health,altroot"; + char *props = default_props; /* check options */ while ((c = getopt(argc, argv, ":Ho:")) != -1) { @@ -2046,7 +2172,7 @@ zpool_do_list(int argc, char **argv) cb.cb_scripted = B_TRUE; break; case 'o': - fields = optarg; + props = optarg; break; case ':': (void) fprintf(stderr, gettext("missing argument for " @@ -2063,29 +2189,17 @@ zpool_do_list(int argc, char **argv) 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++; - } - + if (zprop_get_list(g_zfs, props, &cb.cb_proplist, ZFS_TYPE_POOL) != 0) + usage(B_FALSE); cb.cb_first = B_TRUE; - ret = for_each_pool(argc, argv, B_TRUE, NULL, list_callback, &cb); + ret = for_each_pool(argc, argv, B_TRUE, &cb.cb_proplist, + list_callback, &cb); - if (argc == 0 && cb.cb_first) { + zprop_free_list(cb.cb_proplist); + + if (argc == 0 && cb.cb_first && !cb.cb_scripted) { (void) printf(gettext("no pools available\n")); return (0); } @@ -2128,10 +2242,7 @@ zpool_do_attach_or_replace(int argc, char **argv, int replacing) 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) { @@ -2146,8 +2257,6 @@ zpool_do_attach_or_replace(int argc, char **argv, int replacing) } } - log_argc = argc; - log_argv = argv; argc -= optind; argv += optind; @@ -2190,14 +2299,15 @@ zpool_do_attach_or_replace(int argc, char **argv, int replacing) if ((zhp = zpool_open(g_zfs, poolname)) == NULL) return (1); - if ((config = zpool_get_config(zhp, NULL)) == NULL) { + if (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); + nvroot = make_root_vdev(zhp, force, B_FALSE, replacing, B_FALSE, + argc, argv); if (nvroot == NULL) { zpool_close(zhp); return (1); @@ -2205,11 +2315,6 @@ zpool_do_attach_or_replace(int argc, char **argv, int replacing) 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); @@ -2299,10 +2404,6 @@ zpool_do_detach(int argc, char **argv) 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); @@ -2311,7 +2412,6 @@ zpool_do_detach(int argc, char **argv) /* * zpool online <pool> <device> ... */ -/* ARGSUSED */ int zpool_do_online(int argc, char **argv) { @@ -2319,6 +2419,7 @@ zpool_do_online(int argc, char **argv) char *poolname; zpool_handle_t *zhp; int ret = 0; + vdev_state_t newstate; /* check options */ while ((c = getopt(argc, argv, "t")) != -1) { @@ -2349,17 +2450,26 @@ zpool_do_online(int argc, char **argv) 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 + for (i = 1; i < argc; i++) { + if (zpool_vdev_online(zhp, argv[i], 0, &newstate) == 0) { + if (newstate != VDEV_STATE_HEALTHY) { + (void) printf(gettext("warning: device '%s' " + "onlined, but remains in faulted state\n"), + argv[i]); + if (newstate == VDEV_STATE_FAULTED) + (void) printf(gettext("use 'zpool " + "clear' to restore a faulted " + "device\n")); + else + (void) printf(gettext("use 'zpool " + "replace' to replace devices " + "that are no longer present\n")); + } + } else { ret = 1; - - if (!ret) { - zpool_log_history(g_zfs, argc + optind, argv - optind, poolname, - B_TRUE, B_FALSE); + } } + zpool_close(zhp); return (ret); @@ -2417,17 +2527,11 @@ zpool_do_offline(int argc, char **argv) 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 + for (i = 1; i < argc; i++) { + if (zpool_vdev_offline(zhp, argv[i], istmp) != 0) ret = 1; - - if (!ret) { - zpool_log_history(g_zfs, argc + optind, argv - optind, poolname, - B_TRUE, B_FALSE); } + zpool_close(zhp); return (ret); @@ -2458,14 +2562,12 @@ zpool_do_clear(int argc, char **argv) pool = argv[1]; device = argc == 3 ? argv[2] : NULL; - if ((zhp = zpool_open(g_zfs, pool)) == NULL) + if ((zhp = zpool_open_canfail(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); @@ -2494,11 +2596,6 @@ scrub_callback(zpool_handle_t *zhp, void *data) 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); } @@ -2559,7 +2656,7 @@ print_scrub_status(nvlist_t *nvroot) uint_t vsc; time_t start, end, now; double fraction_done; - uint64_t examined, total, minutes_left; + uint64_t examined, total, minutes_left, minutes_taken; char *scrub_type; verify(nvlist_lookup_uint64_array(nvroot, ZPOOL_CONFIG_STATS, @@ -2583,8 +2680,13 @@ print_scrub_status(nvlist_t *nvroot) total = vs->vs_alloc; if (end != 0) { - (void) printf(gettext("%s %s with %llu errors on %s"), + minutes_taken = (uint64_t)((end - start) / 60); + + (void) printf(gettext("%s %s after %lluh%um with %llu errors " + "on %s"), scrub_type, vs->vs_scrub_complete ? "completed" : "stopped", + (u_longlong_t)(minutes_taken / 60), + (uint_t)(minutes_taken % 60), (u_longlong_t)vs->vs_scrub_errors, ctime(&end)); return; } @@ -2597,9 +2699,12 @@ print_scrub_status(nvlist_t *nvroot) fraction_done = (double)examined / total; minutes_left = (uint64_t)((now - start) * (1 - fraction_done) / fraction_done / 60); + minutes_taken = (uint64_t)((now - start) / 60); - (void) printf(gettext("%s in progress, %.2f%% done, %lluh%um to go\n"), - scrub_type, 100 * fraction_done, + (void) printf(gettext("%s in progress for %lluh%um, %.2f%% done, " + "%lluh%um to go\n"), + scrub_type, (u_longlong_t)(minutes_taken / 60), + (uint_t)(minutes_taken % 60), 100 * fraction_done, (u_longlong_t)(minutes_left / 60), (uint_t)(minutes_left % 60)); } @@ -2653,7 +2758,7 @@ find_spare(zpool_handle_t *zhp, void *data) */ void print_status_config(zpool_handle_t *zhp, const char *name, nvlist_t *nv, - int namewidth, int depth, boolean_t isspare) + int namewidth, int depth, boolean_t isspare, boolean_t print_logs) { nvlist_t **child; uint_t c, children; @@ -2662,7 +2767,7 @@ print_status_config(zpool_handle_t *zhp, const char *name, nvlist_t *nv, char *vname; uint64_t notpresent; spare_cbdata_t cb; - const char *state; + char *state; verify(nvlist_lookup_uint64_array(nv, ZPOOL_CONFIG_STATS, (uint64_t **)&vs, &c) == 0); @@ -2671,7 +2776,7 @@ print_status_config(zpool_handle_t *zhp, const char *name, nvlist_t *nv, &child, &children) != 0) children = 0; - state = state_to_name(vs); + state = zpool_state_to_name(vs->vs_state, vs->vs_aux); if (isspare) { /* * For hot spares, we use the terms 'INUSE' and 'AVAILABLE' for @@ -2736,6 +2841,18 @@ print_status_config(zpool_handle_t *zhp, const char *name, nvlist_t *nv, } break; + case VDEV_AUX_ERR_EXCEEDED: + (void) printf(gettext("too many errors")); + break; + + case VDEV_AUX_IO_FAILURE: + (void) printf(gettext("experienced I/O failures")); + break; + + case VDEV_AUX_BAD_LOG: + (void) printf(gettext("bad intent log")); + break; + default: (void) printf(gettext("corrupted data")); break; @@ -2753,9 +2870,15 @@ print_status_config(zpool_handle_t *zhp, const char *name, nvlist_t *nv, (void) printf("\n"); for (c = 0; c < children; c++) { + uint64_t is_log = B_FALSE; + + (void) nvlist_lookup_uint64(child[c], ZPOOL_CONFIG_IS_LOG, + &is_log); + if ((is_log && !print_logs) || (!is_log && print_logs)) + continue; vname = zpool_vdev_name(g_zfs, zhp, child[c]); print_status_config(zhp, vname, child[c], - namewidth, depth + 2, isspare); + namewidth, depth + 2, isspare, B_FALSE); free(vname); } } @@ -2763,7 +2886,7 @@ print_status_config(zpool_handle_t *zhp, const char *name, nvlist_t *nv, static void print_error_log(zpool_handle_t *zhp) { - nvlist_t *nverrlist; + nvlist_t *nverrlist = NULL; nvpair_t *elem; char *pathname; size_t len = MAXPATHLEN * 2; @@ -2810,7 +2933,27 @@ print_spares(zpool_handle_t *zhp, nvlist_t **spares, uint_t nspares, 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); + namewidth, 2, B_TRUE, B_FALSE); + free(name); + } +} + +static void +print_l2cache(zpool_handle_t *zhp, nvlist_t **l2cache, uint_t nl2cache, + int namewidth) +{ + uint_t i; + char *name; + + if (nl2cache == 0) + return; + + (void) printf(gettext("\tcache\n")); + + for (i = 0; i < nl2cache; i++) { + name = zpool_vdev_name(g_zfs, zhp, l2cache[i]); + print_status_config(zhp, name, l2cache[i], + namewidth, 2, B_FALSE, B_FALSE); free(name); } } @@ -2869,7 +3012,7 @@ status_callback(zpool_handle_t *zhp, void *data) &nvroot) == 0); verify(nvlist_lookup_uint64_array(nvroot, ZPOOL_CONFIG_STATS, (uint64_t **)&vs, &c) == 0); - health = state_to_name(vs); + health = zpool_state_to_name(vs->vs_state, vs->vs_aux); (void) printf(gettext(" pool: %s\n"), zpool_get_name(zhp)); (void) printf(gettext(" state: %s\n"), health); @@ -2972,6 +3115,45 @@ status_callback(zpool_handle_t *zhp, void *data) "backup.\n")); break; + case ZPOOL_STATUS_FAULTED_DEV_R: + (void) printf(gettext("status: One or more devices are " + "faulted in response to persistent errors.\n\tSufficient " + "replicas exist for the pool to continue functioning " + "in a\n\tdegraded state.\n")); + (void) printf(gettext("action: Replace the faulted device, " + "or use 'zpool clear' to mark the device\n\trepaired.\n")); + break; + + case ZPOOL_STATUS_FAULTED_DEV_NR: + (void) printf(gettext("status: One or more devices are " + "faulted in response to persistent errors. There are " + "insufficient replicas for the pool to\n\tcontinue " + "functioning.\n")); + (void) printf(gettext("action: Destroy and re-create the pool " + "from a backup source. Manually marking the device\n" + "\trepaired using 'zpool clear' may allow some data " + "to be recovered.\n")); + break; + + case ZPOOL_STATUS_IO_FAILURE_WAIT: + case ZPOOL_STATUS_IO_FAILURE_CONTINUE: + (void) printf(gettext("status: One or more devices are " + "faulted in response to IO failures.\n")); + (void) printf(gettext("action: Make sure the affected devices " + "are connected, then run 'zpool clear'.\n")); + break; + + case ZPOOL_STATUS_BAD_LOG: + (void) printf(gettext("status: An intent log record " + "could not be read.\n" + "\tWaiting for adminstrator intervention to fix the " + "faulted pool.\n")); + (void) printf(gettext("action: Either restore the affected " + "device(s) and run 'zpool online',\n" + "\tor ignore the intent log records by running " + "'zpool clear'.\n")); + break; + default: /* * The remaining errors can't actually be generated, yet. @@ -2986,8 +3168,8 @@ status_callback(zpool_handle_t *zhp, void *data) if (config != NULL) { int namewidth; uint64_t nerr; - nvlist_t **spares; - uint_t nspares; + nvlist_t **spares, **l2cache; + uint_t nspares, nl2cache; (void) printf(gettext(" scrub: ")); @@ -3001,7 +3183,14 @@ status_callback(zpool_handle_t *zhp, void *data) (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); + namewidth, 0, B_FALSE, B_FALSE); + if (num_logs(nvroot) > 0) + print_status_config(zhp, "logs", nvroot, namewidth, 0, + B_FALSE, B_TRUE); + + if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_L2CACHE, + &l2cache, &nl2cache) == 0) + print_l2cache(zhp, l2cache, nl2cache, namewidth); if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_SPARES, &spares, &nspares) == 0) @@ -3016,7 +3205,7 @@ status_callback(zpool_handle_t *zhp, void *data) * precise count by fetching the entire log and * uniquifying the results. */ - if (nerr < 100 && !cbp->cb_verbose && + if (nerr > 0 && nerr < 100 && !cbp->cb_verbose && zpool_get_errlog(zhp, &nverrlist) == 0) { nvpair_t *elem; @@ -3103,6 +3292,7 @@ typedef struct upgrade_cbdata { int cb_first; int cb_newer; int cb_argc; + uint64_t cb_version; char **cb_argv; } upgrade_cbdata_t; @@ -3118,7 +3308,7 @@ upgrade_cb(zpool_handle_t *zhp, void *arg) verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_VERSION, &version) == 0); - if (!cbp->cb_newer && version < ZFS_VERSION) { + if (!cbp->cb_newer && version < SPA_VERSION) { if (!cbp->cb_all) { if (cbp->cb_first) { (void) printf(gettext("The following pools are " @@ -3135,16 +3325,13 @@ upgrade_cb(zpool_handle_t *zhp, void *arg) zpool_get_name(zhp)); } else { cbp->cb_first = B_FALSE; - ret = zpool_upgrade(zhp); + ret = zpool_upgrade(zhp, cbp->cb_version); 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)); + "'%s'\n\n"), zpool_get_name(zhp)); } } - } else if (cbp->cb_newer && version > ZFS_VERSION) { + } else if (cbp->cb_newer && version > SPA_VERSION) { assert(!cbp->cb_all); if (cbp->cb_first) { @@ -3168,29 +3355,37 @@ upgrade_cb(zpool_handle_t *zhp, void *arg) static int upgrade_one(zpool_handle_t *zhp, void *data) { - nvlist_t *config; - uint64_t version; - int ret; upgrade_cbdata_t *cbp = data; + uint64_t cur_version; + int ret; - config = zpool_get_config(zhp, NULL); - verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_VERSION, - &version) == 0); + if (strcmp("log", zpool_get_name(zhp)) == 0) { + (void) printf(gettext("'log' is now a reserved word\n" + "Pool 'log' must be renamed using export and import" + " to upgrade.\n")); + return (1); + } - if (version == ZFS_VERSION) { + cur_version = zpool_get_prop_int(zhp, ZPOOL_PROP_VERSION, NULL); + if (cur_version > cbp->cb_version) { + (void) printf(gettext("Pool '%s' is already formatted " + "using more current version '%llu'.\n"), + zpool_get_name(zhp), cur_version); + return (0); + } + if (cur_version == cbp->cb_version) { (void) printf(gettext("Pool '%s' is already formatted " "using the current version.\n"), zpool_get_name(zhp)); return (0); } - ret = zpool_upgrade(zhp); + ret = zpool_upgrade(zhp, cbp->cb_version); 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); + "from version %llu to version %llu\n\n"), + zpool_get_name(zhp), (u_longlong_t)cur_version, + (u_longlong_t)cbp->cb_version); } return (ret != 0); @@ -3199,7 +3394,7 @@ upgrade_one(zpool_handle_t *zhp, void *data) /* * zpool upgrade * zpool upgrade -v - * zpool upgrade <-a | pool> + * zpool upgrade [-V version] <-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 @@ -3212,9 +3407,11 @@ zpool_do_upgrade(int argc, char **argv) upgrade_cbdata_t cb = { 0 }; int ret = 0; boolean_t showversions = B_FALSE; + char *end; + /* check options */ - while ((c = getopt(argc, argv, "av")) != -1) { + while ((c = getopt(argc, argv, "avV:")) != -1) { switch (c) { case 'a': cb.cb_all = B_TRUE; @@ -3222,6 +3419,15 @@ zpool_do_upgrade(int argc, char **argv) case 'v': showversions = B_TRUE; break; + case 'V': + cb.cb_version = strtoll(optarg, &end, 10); + if (*end != '\0' || cb.cb_version > SPA_VERSION || + cb.cb_version < SPA_VERSION_1) { + (void) fprintf(stderr, + gettext("invalid version '%s'\n"), optarg); + usage(B_FALSE); + } + break; case '?': (void) fprintf(stderr, gettext("invalid option '%c'\n"), optopt); @@ -3234,6 +3440,14 @@ zpool_do_upgrade(int argc, char **argv) argc -= optind; argv += optind; + if (cb.cb_version == 0) { + cb.cb_version = SPA_VERSION; + } else if (!cb.cb_all && argc == 0) { + (void) fprintf(stderr, gettext("-V option is " + "incompatible with other arguments\n")); + usage(B_FALSE); + } + if (showversions) { if (cb.cb_all || argc != 0) { (void) fprintf(stderr, gettext("-v option is " @@ -3242,14 +3456,14 @@ zpool_do_upgrade(int argc, char **argv) } } else if (cb.cb_all) { if (argc != 0) { - (void) fprintf(stderr, gettext("-a option is " - "incompatible with other arguments\n")); + (void) fprintf(stderr, gettext("-a option should not " + "be used along with a pool name\n")); usage(B_FALSE); } } - (void) printf(gettext("This system is currently running ZFS version " - "%llu.\n\n"), ZFS_VERSION); + (void) printf(gettext("This system is currently running " + "ZFS pool version %llu.\n\n"), SPA_VERSION); cb.cb_first = B_TRUE; if (showversions) { (void) printf(gettext("The following versions are " @@ -3265,8 +3479,16 @@ zpool_do_upgrade(int argc, char **argv) (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 " + (void) printf(gettext(" 6 bootfs pool property\n")); + (void) printf(gettext(" 7 Separate intent log devices\n")); + (void) printf(gettext(" 8 Delegated administration\n")); + (void) printf(gettext(" 9 refquota and refreservation " + "properties\n")); + (void) printf(gettext(" 10 Cache devices\n")); + (void) printf(gettext(" 11 Improved scrub performance\n")); + (void) printf(gettext(" 12 Snapshot properties\n")); + (void) printf(gettext(" 13 snapused property\n")); + (void) printf(gettext("For 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"); @@ -3306,6 +3528,53 @@ zpool_do_upgrade(int argc, char **argv) return (ret); } +typedef struct hist_cbdata { + boolean_t first; + int longfmt; + int internal; +} hist_cbdata_t; + +char *hist_event_table[LOG_END] = { + "invalid event", + "pool create", + "vdev add", + "pool remove", + "pool destroy", + "pool export", + "pool import", + "vdev attach", + "vdev replace", + "vdev detach", + "vdev online", + "vdev offline", + "vdev upgrade", + "pool clear", + "pool scrub", + "pool property set", + "create", + "clone", + "destroy", + "destroy_begin_sync", + "inherit", + "property set", + "quota set", + "permission update", + "permission remove", + "permission who remove", + "promote", + "receive", + "rename", + "reservation set", + "replay_inc_sync", + "replay_full_sync", + "rollback", + "snapshot", + "filesystem version upgrade", + "refquota set", + "refreservation set", + "pool scrub done", +}; + /* * Print out the command history for a specific pool. */ @@ -3316,13 +3585,22 @@ get_history_one(zpool_handle_t *zhp, void *data) nvlist_t **records; uint_t numrecords; char *cmdstr; + char *pathstr; uint64_t dst_time; time_t tsec; struct tm t; char tbuf[30]; int ret, i; + uint64_t who; + struct passwd *pwd; + char *hostname; + char *zonename; + char internalstr[MAXPATHLEN]; + hist_cbdata_t *cb = (hist_cbdata_t *)data; + uint64_t txg; + uint64_t ievent; - *(boolean_t *)data = B_FALSE; + cb->first = B_FALSE; (void) printf(gettext("History for '%s':\n"), zpool_get_name(zhp)); @@ -3333,14 +3611,65 @@ get_history_one(zpool_handle_t *zhp, void *data) &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); + &dst_time) != 0) + continue; + + /* is it an internal event or a standard event? */ + if (nvlist_lookup_string(records[i], ZPOOL_HIST_CMD, + &cmdstr) != 0) { + if (cb->internal == 0) + continue; + + if (nvlist_lookup_uint64(records[i], + ZPOOL_HIST_INT_EVENT, &ievent) != 0) + continue; + verify(nvlist_lookup_uint64(records[i], + ZPOOL_HIST_TXG, &txg) == 0); + verify(nvlist_lookup_string(records[i], + ZPOOL_HIST_INT_STR, &pathstr) == 0); + if (ievent >= LOG_END) + continue; + (void) snprintf(internalstr, + sizeof (internalstr), + "[internal %s txg:%lld] %s", + hist_event_table[ievent], txg, + pathstr); + cmdstr = internalstr; + } + tsec = dst_time; + (void) localtime_r(&tsec, &t); + (void) strftime(tbuf, sizeof (tbuf), "%F.%T", &t); + (void) printf("%s %s", tbuf, cmdstr); + + if (!cb->longfmt) { + (void) printf("\n"); + continue; } + (void) printf(" ["); + if (nvlist_lookup_uint64(records[i], + ZPOOL_HIST_WHO, &who) == 0) { + pwd = getpwuid((uid_t)who); + if (pwd) + (void) printf("user %s on", + pwd->pw_name); + else + (void) printf("user %d on", + (int)who); + } else { + (void) printf(gettext("no info]\n")); + continue; + } + if (nvlist_lookup_string(records[i], + ZPOOL_HIST_HOST, &hostname) == 0) { + (void) printf(" %s", hostname); + } + if (nvlist_lookup_string(records[i], + ZPOOL_HIST_ZONE, &zonename) == 0) { + (void) printf(":%s", zonename); + } + + (void) printf("]"); + (void) printf("\n"); } (void) printf("\n"); nvlist_free(nvhis); @@ -3353,19 +3682,38 @@ get_history_one(zpool_handle_t *zhp, void *data) * * Displays the history of commands that modified pools. */ + + int zpool_do_history(int argc, char **argv) { - boolean_t first = B_TRUE; + hist_cbdata_t cbdata = { 0 }; int ret; + int c; + cbdata.first = B_TRUE; + /* check options */ + while ((c = getopt(argc, argv, "li")) != -1) { + switch (c) { + case 'l': + cbdata.longfmt = 1; + break; + case 'i': + cbdata.internal = 1; + break; + case '?': + (void) fprintf(stderr, gettext("invalid option '%c'\n"), + optopt); + usage(B_FALSE); + } + } argc -= optind; argv += optind; ret = for_each_pool(argc, argv, B_FALSE, NULL, get_history_one, - &first); + &cbdata); - if (argc == 0 && first == B_TRUE) { + if (argc == 0 && cbdata.first == B_TRUE) { (void) printf(gettext("no pools available\n")); return (0); } @@ -3376,17 +3724,18 @@ zpool_do_history(int argc, char **argv) static int get_callback(zpool_handle_t *zhp, void *data) { - libzfs_get_cbdata_t *cbp = (libzfs_get_cbdata_t *)data; + zprop_get_cbdata_t *cbp = (zprop_get_cbdata_t *)data; char value[MAXNAMELEN]; - zfs_source_t srctype; - zpool_proplist_t *pl; + zprop_source_t srctype; + zprop_list_t *pl; for (pl = cbp->cb_proplist; pl != NULL; pl = pl->pl_next) { /* - * Skip the special fake placeholder. + * Skip the special fake placeholder. This will also skip + * over the name property when 'all' is specified. */ - if (pl->pl_prop == ZFS_PROP_NAME && + if (pl->pl_prop == ZPOOL_PROP_NAME && pl == cbp->cb_proplist) continue; @@ -3394,7 +3743,7 @@ get_callback(zpool_handle_t *zhp, void *data) value, sizeof (value), &srctype) != 0) continue; - libzfs_print_one_property(zpool_get_name(zhp), cbp, + zprop_print_one_property(zpool_get_name(zhp), cbp, zpool_prop_to_name(pl->pl_prop), value, srctype, NULL); } return (0); @@ -3403,25 +3752,27 @@ get_callback(zpool_handle_t *zhp, void *data) int zpool_do_get(int argc, char **argv) { - libzfs_get_cbdata_t cb = { 0 }; - zpool_proplist_t fake_name = { 0 }; + zprop_get_cbdata_t cb = { 0 }; + zprop_list_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_sources = ZPROP_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; + cb.cb_type = ZFS_TYPE_POOL; - if (zpool_get_proplist(g_zfs, argv[1], &cb.cb_proplist) != 0) + if (zprop_get_list(g_zfs, argv[1], &cb.cb_proplist, + ZFS_TYPE_POOL) != 0) usage(B_FALSE); if (cb.cb_proplist != NULL) { - fake_name.pl_prop = ZFS_PROP_NAME; + fake_name.pl_prop = ZPOOL_PROP_NAME; fake_name.pl_width = strlen(gettext("NAME")); fake_name.pl_next = cb.cb_proplist; cb.cb_proplist = &fake_name; @@ -3431,9 +3782,9 @@ zpool_do_get(int argc, char **argv) get_callback, &cb); if (cb.cb_proplist == &fake_name) - zfs_free_proplist(fake_name.pl_next); + zprop_free_list(fake_name.pl_next); else - zfs_free_proplist(cb.cb_proplist); + zprop_free_list(cb.cb_proplist); return (ret); } @@ -3500,11 +3851,6 @@ zpool_do_set(int argc, char **argv) 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); } @@ -3531,7 +3877,6 @@ main(int argc, char **argv) int ret; int i; char *cmdname; - int found = 0; (void) setlocale(LC_ALL, ""); (void) textdomain(TEXT_DOMAIN); @@ -3562,26 +3907,29 @@ main(int argc, char **argv) if (strcmp(cmdname, "-?") == 0) usage(B_TRUE); + zpool_set_history_str("zpool", argc, argv, history_str); + verify(zpool_stage_history(g_zfs, history_str) == 0); + /* * 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) { + } else if (strchr(cmdname, '=')) { + verify(find_command_idx("set", &i) == 0); + current_command = &command_table[i]; + ret = command_table[i].func(argc, argv); + } else if (strcmp(cmdname, "freeze") == 0 && argc == 3) { + /* + * 'freeze' is a vile debugging abomination, so we treat + * it as such. + */ 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) { + } else { (void) fprintf(stderr, gettext("unrecognized " "command '%s'\n"), cmdname); usage(B_FALSE); |