/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
+ * Copyright (c) 2012 by Delphix. All rights reserved.
+ * Copyright (c) 2012 by Frederik Wessels. All rights reserved.
+ * Copyright (c) 2012 by Cyril Plisko. All rights reserved.
*/
#include <assert.h>
#include <priv.h>
#include <pwd.h>
#include <zone.h>
+#include <zfs_prop.h>
#include <sys/fs/zfs.h>
#include <sys/stat.h>
+#include <sys/fm/util.h>
+#include <sys/fm/protocol.h>
#include <libzfs.h>
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_reopen(int, char **);
+
+static int zpool_do_reguid(int, char **);
static int zpool_do_attach(int, char **);
static int zpool_do_detach(int, char **);
static int zpool_do_upgrade(int, char **);
static int zpool_do_history(int, char **);
+static int zpool_do_events(int, char **);
static int zpool_do_get(int, char **);
static int zpool_do_set(int, char **);
HELP_SCRUB,
HELP_STATUS,
HELP_UPGRADE,
+ HELP_EVENTS,
HELP_GET,
HELP_SET,
- HELP_SPLIT
+ HELP_SPLIT,
+ HELP_REGUID,
+ HELP_REOPEN
} zpool_help_t;
{ "online", zpool_do_online, HELP_ONLINE },
{ "offline", zpool_do_offline, HELP_OFFLINE },
{ "clear", zpool_do_clear, HELP_CLEAR },
+ { "reopen", zpool_do_reopen, HELP_REOPEN },
{ NULL },
{ "attach", zpool_do_attach, HELP_ATTACH },
{ "detach", zpool_do_detach, HELP_DETACH },
{ "import", zpool_do_import, HELP_IMPORT },
{ "export", zpool_do_export, HELP_EXPORT },
{ "upgrade", zpool_do_upgrade, HELP_UPGRADE },
+ { "reguid", zpool_do_reguid, HELP_REGUID },
{ NULL },
{ "history", zpool_do_history, HELP_HISTORY },
+ { "events", zpool_do_events, HELP_EVENTS },
+ { NULL },
{ "get", zpool_do_get, HELP_GET },
{ "set", zpool_do_set, HELP_SET },
};
get_usage(zpool_help_t idx) {
switch (idx) {
case HELP_ADD:
- return (gettext("\tadd [-fn] <pool> <vdev> ...\n"));
+ return (gettext("\tadd [-fn] [-o property=value] "
+ "<pool> <vdev> ...\n"));
case HELP_ATTACH:
- return (gettext("\tattach [-f] <pool> <device> "
- "<new-device>\n"));
+ return (gettext("\tattach [-f] [-o property=value] "
+ "<pool> <device> <new-device>\n"));
case HELP_CLEAR:
return (gettext("\tclear [-nF] <pool> [device]\n"));
case HELP_CREATE:
"[new-device]\n"));
case HELP_REMOVE:
return (gettext("\tremove <pool> <device> ...\n"));
+ case HELP_REOPEN:
+ return (""); /* Undocumented command */
case HELP_SCRUB:
return (gettext("\tscrub [-s] <pool> ...\n"));
case HELP_STATUS:
return (gettext("\tupgrade\n"
"\tupgrade -v\n"
"\tupgrade [-V version] <-a | pool ...>\n"));
+ case HELP_EVENTS:
+ return (gettext("\tevents [-vHfc]\n"));
case HELP_GET:
return (gettext("\tget <\"all\" | property[,...]> "
"<pool> ...\n"));
return (gettext("\tsplit [-n] [-R altroot] [-o mntopts]\n"
"\t [-o property=value] <pool> <newpool> "
"[<device> ...]\n"));
+ case HELP_REGUID:
+ return (gettext("\treguid <pool>\n"));
}
abort();
}
/*
- * zpool add [-fn] <pool> <vdev> ...
+ * zpool add [-fn] [-o property=value] <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.
+ * -o Set property=value.
*
* 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
int ret;
zpool_handle_t *zhp;
nvlist_t *config;
+ nvlist_t *props = NULL;
+ char *propval;
/* check options */
- while ((c = getopt(argc, argv, "fn")) != -1) {
+ while ((c = getopt(argc, argv, "fno:")) != -1) {
switch (c) {
case 'f':
force = B_TRUE;
case 'n':
dryrun = B_TRUE;
break;
+ case 'o':
+ if ((propval = strchr(optarg, '=')) == NULL) {
+ (void) fprintf(stderr, gettext("missing "
+ "'=' for -o option\n"));
+ usage(B_FALSE);
+ }
+ *propval = '\0';
+ propval++;
+
+ if ((strcmp(optarg, ZPOOL_CONFIG_ASHIFT) != 0) ||
+ (add_prop_list(optarg, propval, &props, B_TRUE)))
+ usage(B_FALSE);
+ break;
case '?':
(void) fprintf(stderr, gettext("invalid option '%c'\n"),
optopt);
}
/* pass off to get_vdev_spec for processing */
- nvroot = make_root_vdev(zhp, force, !force, B_FALSE, dryrun,
+ nvroot = make_root_vdev(zhp, props, force, !force, B_FALSE, dryrun,
argc, argv);
if (nvroot == NULL) {
zpool_close(zhp);
ret = (zpool_add(zhp, nvroot) != 0);
}
+ nvlist_free(props);
nvlist_free(nvroot);
zpool_close(zhp);
}
/* pass off to get_vdev_spec for bulk processing */
- nvroot = make_root_vdev(NULL, force, !force, B_FALSE, dryrun,
+ nvroot = make_root_vdev(NULL, props, force, !force, B_FALSE, dryrun,
argc - 1, argv + 1);
if (nvroot == NULL)
goto errout;
const char *health;
uint_t vsc;
int namewidth;
+ char *comment;
verify(nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME,
&name) == 0);
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);
+ (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");
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"));
+ (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 "
+ (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"));
+ (void) printf(
+ gettext(" status: The pool data is corrupted.\n"));
break;
case ZPOOL_STATUS_OFFLINE_DEV:
- (void) printf(gettext("status: One or more devices "
+ (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 "
+ (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 "
+ (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 "
+ (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 "
+ (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 "
+ (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 "
+ (void) printf(gettext(" status: An intent log record cannot be "
"read.\n"));
break;
case ZPOOL_STATUS_RESILVERING:
- (void) printf(gettext("status: One or more devices were being "
+ (void) printf(gettext(" status: One or more devices were being "
"resilvered.\n"));
break;
*/
if (vs->vs_state == VDEV_STATE_HEALTHY) {
if (reason == ZPOOL_STATUS_VERSION_OLDER)
- (void) printf(gettext("action: The pool can be "
+ (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 "
+ (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 "
+ (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 "
+ (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 "
+ (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"));
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 "
+ (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 "
+ (void) printf(gettext(" action: The pool cannot be "
"imported due to damaged devices or data.\n"));
}
}
+ /* Print the comment attached to the pool. */
+ if (nvlist_lookup_string(config, ZPOOL_CONFIG_COMMENT, &comment) == 0)
+ (void) printf(gettext("comment: %s\n"), comment);
+
/*
* If the state is "closed" or "can't open", and the aux state
* is "corrupt data":
}
if (msgid != NULL)
- (void) printf(gettext(" see: http://www.sun.com/msg/%s\n"),
+ (void) printf(gettext(" see: http://zfsonlinux.org/msg/%s\n"),
msgid);
- (void) printf(gettext("config:\n\n"));
+ (void) printf(gettext(" config:\n\n"));
namewidth = max_width(NULL, nvroot, 0, 0);
if (namewidth < 10)
if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_HOSTID,
&hostid) == 0) {
- if ((unsigned long)hostid != gethostid()) {
+ unsigned long system_hostid = gethostid() & 0xffffffff;
+
+ if ((unsigned long)hostid != system_hostid) {
char *hostname;
uint64_t timestamp;
time_t t;
* -c Read pool information from a cachefile instead of searching
* devices.
*
- * -d Scan in a specific directory, other than /dev/dsk. More than
+ * -d Scan in a specific directory, other than /dev/. More than
* one directory can be specified using multiple '-d' options.
*
* -D Scan for previously destroyed pools or import all or only
zpool_do_import(int argc, char **argv)
{
char **searchdirs = NULL;
+ char *env, *envdup = NULL;
int nsearch = 0;
int c;
int err = 0;
nvlist_add_uint32(policy, ZPOOL_REWIND_REQUEST, rewind_policy) != 0)
goto error;
- if (searchdirs == NULL) {
- searchdirs = safe_malloc(sizeof (char *));
- searchdirs[0] = "/dev/dsk";
- nsearch = 1;
- }
-
/* check argument count */
if (do_all) {
if (argc != 0) {
if (argc == 0 && !priv_ineffect(PRIV_SYS_CONFIG)) {
(void) fprintf(stderr, gettext("cannot "
"discover pools: permission denied\n"));
- free(searchdirs);
+ if (searchdirs != NULL)
+ free(searchdirs);
+
nvlist_free(policy);
return (1);
}
idata.unique = B_TRUE;
}
+ /*
+ * Check the environment for the preferred search path.
+ */
+ if ((searchdirs == NULL) && (env = getenv("ZPOOL_IMPORT_PATH"))) {
+ char *dir;
+
+ envdup = strdup(env);
+
+ dir = strtok(envdup, ":");
+ while (dir != NULL) {
+ 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++] = dir;
+ dir = strtok(NULL, ":");
+ }
+ }
idata.path = searchdirs;
idata.paths = nsearch;
}
if (err == 1) {
- free(searchdirs);
+ if (searchdirs != NULL)
+ free(searchdirs);
+ if (envdup != NULL)
+ free(envdup);
nvlist_free(policy);
return (1);
}
nvlist_free(props);
nvlist_free(pools);
nvlist_free(policy);
- free(searchdirs);
+ if (searchdirs != NULL)
+ free(searchdirs);
+ if (envdup != NULL)
+ free(envdup);
return (err ? 1 : 0);
}
typedef struct iostat_cbdata {
- zpool_list_t *cb_list;
- int cb_verbose;
- int cb_iteration;
+ boolean_t cb_verbose;
int cb_namewidth;
+ int cb_iteration;
+ zpool_list_t *cb_list;
} iostat_cbdata_t;
static void
return;
for (c = 0; c < children; c++) {
- uint64_t ishole = B_FALSE;
+ uint64_t ishole = B_FALSE, islog = B_FALSE;
- if (nvlist_lookup_uint64(newchild[c],
- ZPOOL_CONFIG_IS_HOLE, &ishole) == 0 && ishole)
+ (void) nvlist_lookup_uint64(newchild[c], ZPOOL_CONFIG_IS_HOLE,
+ &ishole);
+
+ (void) nvlist_lookup_uint64(newchild[c], ZPOOL_CONFIG_IS_LOG,
+ &islog);
+
+ if (ishole || islog)
continue;
vname = zpool_vdev_name(g_zfs, zhp, newchild[c], B_FALSE);
}
/*
+ * Log device section
+ */
+
+ if (num_logs(newnv) > 0) {
+ (void) printf("%-*s - - - - - "
+ "-\n", cb->cb_namewidth, "logs");
+
+ for (c = 0; c < children; c++) {
+ uint64_t islog = B_FALSE;
+ (void) nvlist_lookup_uint64(newchild[c],
+ ZPOOL_CONFIG_IS_LOG, &islog);
+
+ if (islog) {
+ vname = zpool_vdev_name(g_zfs, zhp, newchild[c],
+ B_FALSE);
+ print_vdev_stats(zhp, vname, oldnv ?
+ oldchild[c] : NULL, newchild[c],
+ cb, depth + 2);
+ free(vname);
+ }
+ }
+
+ }
+
+ /*
* Include level 2 ARC devices in iostat output
*/
if (nvlist_lookup_nvlist_array(newnv, ZPOOL_CONFIG_L2CACHE,
return (0);
}
+static int
+get_columns(void)
+{
+ struct winsize ws;
+ int columns = 80;
+ int error;
+
+ if (isatty(STDOUT_FILENO)) {
+ error = ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws);
+ if (error == 0)
+ columns = ws.ws_col;
+ } else {
+ columns = 999;
+ }
+
+ return columns;
+}
+
int
get_namewidth(zpool_handle_t *zhp, void *data)
{
iostat_cbdata_t *cb = data;
nvlist_t *config, *nvroot;
+ int columns;
if ((config = zpool_get_config(zhp, NULL)) != NULL) {
verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
if (!cb->cb_verbose)
cb->cb_namewidth = strlen(zpool_get_name(zhp));
else
- cb->cb_namewidth = max_width(zhp, nvroot, 0, 0);
+ cb->cb_namewidth = max_width(zhp, nvroot, 0,
+ cb->cb_namewidth);
}
/*
- * The width must fall into the range [10,38]. The upper limit is the
- * maximum we can have and still fit in 80 columns.
+ * The width must be at least 10, but may be as large as the
+ * column width - 42 so that we can still fit in one line.
*/
+ columns = get_columns();
+
if (cb->cb_namewidth < 10)
cb->cb_namewidth = 10;
- if (cb->cb_namewidth > 38)
- cb->cb_namewidth = 38;
+ if (cb->cb_namewidth > columns - 42)
+ cb->cb_namewidth = columns - 42;
return (0);
}
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);
+ (void) fprintf(stderr, gettext("no pools available\n"));
+ else {
+ /*
+ * 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);
+ /*
+ * 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 (timestamp_fmt != NODATE)
- print_timestamp(timestamp_fmt);
+ if (timestamp_fmt != NODATE)
+ print_timestamp(timestamp_fmt);
- /*
- * If it's the first time, or verbose mode, print the header.
- */
- if (++cb.cb_iteration == 1 || verbose)
- print_iostat_header(&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);
+ (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 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");
+ if (verbose)
+ (void) printf("\n");
+ }
/*
* Flush the output so that redirection to a file isn't buffered
}
typedef struct list_cbdata {
+ boolean_t cb_verbose;
+ int cb_namewidth;
boolean_t cb_scripted;
- boolean_t cb_first;
zprop_list_t *cb_proplist;
} list_cbdata_t;
* Given a list of columns to display, output appropriate headers for each one.
*/
static void
-print_header(zprop_list_t *pl)
+print_header(list_cbdata_t *cb)
{
+ zprop_list_t *pl = cb->cb_proplist;
const char *header;
boolean_t first = B_TRUE;
boolean_t right_justify;
+ size_t width = 0;
for (; pl != NULL; pl = pl->pl_next) {
if (pl->pl_prop == ZPROP_INVAL)
continue;
+ width = pl->pl_width;
+ if (first && cb->cb_verbose) {
+ /*
+ * Reset the width to accommodate the verbose listing
+ * of devices.
+ */
+ width = cb->cb_namewidth;
+ }
+
if (!first)
(void) printf(" ");
else
if (pl->pl_next == NULL && !right_justify)
(void) printf("%s", header);
else if (right_justify)
- (void) printf("%*s", (int)pl->pl_width, header);
+ (void) printf("%*s", (int)width, header);
else
- (void) printf("%-*s", (int)pl->pl_width, header);
+ (void) printf("%-*s", (int)width, header);
}
(void) printf("\n");
* to the described layout.
*/
static void
-print_pool(zpool_handle_t *zhp, zprop_list_t *pl, int scripted)
+print_pool(zpool_handle_t *zhp, list_cbdata_t *cb)
{
+ zprop_list_t *pl = cb->cb_proplist;
boolean_t first = B_TRUE;
char property[ZPOOL_MAXPROPLEN];
char *propstr;
boolean_t right_justify;
- int width;
+ size_t width;
for (; pl != NULL; pl = pl->pl_next) {
+
+ width = pl->pl_width;
+ if (first && cb->cb_verbose) {
+ /*
+ * Reset the width to accommodate the verbose listing
+ * of devices.
+ */
+ width = cb->cb_namewidth;
+ }
+
if (!first) {
- if (scripted)
+ if (cb->cb_scripted)
(void) printf("\t");
else
(void) printf(" ");
right_justify = B_FALSE;
if (pl->pl_prop != ZPROP_INVAL) {
- if (zpool_get_prop(zhp, pl->pl_prop, property,
+ if (pl->pl_prop == ZPOOL_PROP_EXPANDSZ &&
+ zpool_get_prop_int(zhp, pl->pl_prop, NULL) == 0)
+ propstr = "-";
+ else if (zpool_get_prop(zhp, pl->pl_prop, property,
sizeof (property), NULL) != 0)
propstr = "-";
else
propstr = "-";
}
- width = pl->pl_width;
/*
* 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))
+ if (cb->cb_scripted || (pl->pl_next == NULL && !right_justify))
(void) printf("%s", propstr);
else if (right_justify)
- (void) printf("%*s", width, propstr);
+ (void) printf("%*s", (int)width, propstr);
else
- (void) printf("%-*s", width, propstr);
+ (void) printf("%-*s", (int)width, propstr);
}
(void) printf("\n");
}
+static void
+print_one_column(zpool_prop_t prop, uint64_t value, boolean_t scripted)
+{
+ char propval[64];
+ boolean_t fixed;
+ size_t width = zprop_width(prop, &fixed, ZFS_TYPE_POOL);
+
+ zfs_nicenum(value, propval, sizeof (propval));
+
+ if (prop == ZPOOL_PROP_EXPANDSZ && value == 0)
+ (void) strlcpy(propval, "-", sizeof (propval));
+
+ if (scripted)
+ (void) printf("\t%s", propval);
+ else
+ (void) printf(" %*s", (int)width, propval);
+}
+
+void
+print_list_stats(zpool_handle_t *zhp, const char *name, nvlist_t *nv,
+ list_cbdata_t *cb, int depth)
+{
+ nvlist_t **child;
+ vdev_stat_t *vs;
+ uint_t c, children;
+ char *vname;
+ boolean_t scripted = cb->cb_scripted;
+
+ verify(nvlist_lookup_uint64_array(nv, ZPOOL_CONFIG_VDEV_STATS,
+ (uint64_t **)&vs, &c) == 0);
+
+ if (name != NULL) {
+ if (scripted)
+ (void) printf("\t%s", name);
+ else 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), "");
+
+ /* only toplevel vdevs have capacity stats */
+ if (vs->vs_space == 0) {
+ if (scripted)
+ (void) printf("\t-\t-\t-");
+ else
+ (void) printf(" - - -");
+ } else {
+ print_one_column(ZPOOL_PROP_SIZE, vs->vs_space,
+ scripted);
+ print_one_column(ZPOOL_PROP_CAPACITY, vs->vs_alloc,
+ scripted);
+ print_one_column(ZPOOL_PROP_FREE,
+ vs->vs_space - vs->vs_alloc, scripted);
+ }
+ print_one_column(ZPOOL_PROP_EXPANDSZ, vs->vs_esize,
+ scripted);
+ (void) printf("\n");
+ }
+
+ if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
+ &child, &children) != 0)
+ return;
+
+ for (c = 0; c < children; c++) {
+ uint64_t ishole = B_FALSE;
+
+ if (nvlist_lookup_uint64(child[c],
+ ZPOOL_CONFIG_IS_HOLE, &ishole) == 0 && ishole)
+ continue;
+
+ vname = zpool_vdev_name(g_zfs, zhp, child[c], B_FALSE);
+ print_list_stats(zhp, vname, child[c], cb, depth + 2);
+ free(vname);
+ }
+
+ /*
+ * Include level 2 ARC devices in iostat output
+ */
+ if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_L2CACHE,
+ &child, &children) != 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, child[c],
+ B_FALSE);
+ print_list_stats(zhp, vname, child[c], cb, depth + 2);
+ free(vname);
+ }
+ }
+}
+
+
/*
* Generic callback function to list a pool.
*/
list_callback(zpool_handle_t *zhp, void *data)
{
list_cbdata_t *cbp = data;
+ nvlist_t *config;
+ nvlist_t *nvroot;
- if (cbp->cb_first) {
- if (!cbp->cb_scripted)
- print_header(cbp->cb_proplist);
- cbp->cb_first = B_FALSE;
- }
+ config = zpool_get_config(zhp, NULL);
+
+ print_pool(zhp, cbp);
+ if (!cbp->cb_verbose)
+ return (0);
- print_pool(zhp, cbp->cb_proplist, cbp->cb_scripted);
+ verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
+ &nvroot) == 0);
+ print_list_stats(zhp, NULL, nvroot, cbp, 0);
return (0);
}
int ret;
list_cbdata_t cb = { 0 };
static char default_props[] =
- "name,size,allocated,free,capacity,dedupratio,health,altroot";
+ "name,size,allocated,free,capacity,dedupratio,"
+ "health,altroot";
char *props = default_props;
unsigned long interval = 0, count = 0;
+ zpool_list_t *list;
+ boolean_t first = B_TRUE;
/* check options */
- while ((c = getopt(argc, argv, ":Ho:T:")) != -1) {
+ while ((c = getopt(argc, argv, ":Ho:T:v")) != -1) {
switch (c) {
case 'H':
cb.cb_scripted = B_TRUE;
case 'T':
get_timestamp_arg(*optarg);
break;
+ case 'v':
+ cb.cb_verbose = B_TRUE;
+ break;
case ':':
(void) fprintf(stderr, gettext("missing argument for "
"'%c' option\n"), optopt);
if (zprop_get_list(g_zfs, props, &cb.cb_proplist, ZFS_TYPE_POOL) != 0)
usage(B_FALSE);
- cb.cb_first = B_TRUE;
+ if ((list = pool_list_get(argc, argv, &cb.cb_proplist, &ret)) == NULL)
+ return (1);
+
+ if (argc == 0 && !cb.cb_scripted && pool_list_count(list) == 0) {
+ (void) printf(gettext("no pools available\n"));
+ zprop_free_list(cb.cb_proplist);
+ return (0);
+ }
for (;;) {
+ pool_list_update(list);
+
+ if (pool_list_count(list) == 0)
+ break;
if (timestamp_fmt != NODATE)
print_timestamp(timestamp_fmt);
- ret = for_each_pool(argc, argv, B_TRUE, &cb.cb_proplist,
- list_callback, &cb);
-
- if (argc == 0 && cb.cb_first && !cb.cb_scripted) {
- (void) printf(gettext("no pools available\n"));
- zprop_free_list(cb.cb_proplist);
- return (0);
+ if (!cb.cb_scripted && (first || cb.cb_verbose)) {
+ print_header(&cb);
+ first = B_FALSE;
}
+ ret = pool_list_iter(list, B_TRUE, list_callback, &cb);
if (interval == 0)
break;
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, "/dev/dsk/", 9) == 0)
- name += 9;
- if (strncmp(path, "/dev/dsk/", 9) == 0)
- path += 9;
- 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)
{
nvlist_t *nvroot;
char *poolname, *old_disk, *new_disk;
zpool_handle_t *zhp;
+ nvlist_t *props = NULL;
+ char *propval;
int ret;
/* check options */
- while ((c = getopt(argc, argv, "f")) != -1) {
+ while ((c = getopt(argc, argv, "fo:")) != -1) {
switch (c) {
case 'f':
force = B_TRUE;
break;
+ case 'o':
+ if ((propval = strchr(optarg, '=')) == NULL) {
+ (void) fprintf(stderr, gettext("missing "
+ "'=' for -o option\n"));
+ usage(B_FALSE);
+ }
+ *propval = '\0';
+ propval++;
+
+ if ((strcmp(optarg, ZPOOL_CONFIG_ASHIFT) != 0) ||
+ (add_prop_list(optarg, propval, &props, B_TRUE)))
+ usage(B_FALSE);
+ break;
case '?':
(void) fprintf(stderr, gettext("invalid option '%c'\n"),
optopt);
return (1);
}
- nvroot = make_root_vdev(zhp, force, B_FALSE, replacing, B_FALSE,
+ nvroot = make_root_vdev(zhp, props, force, B_FALSE, replacing, B_FALSE,
argc, argv);
if (nvroot == NULL) {
zpool_close(zhp);
}
/*
- * zpool attach [-f] <pool> <device> <new_device>
+ * zpool attach [-f] [-o property=value] <pool> <device> <new_device>
*
* -f Force attach, even if <new_device> appears to be in use.
+ * -o Set property=value.
*
* 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
if (zpool_get_state(zhp) != POOL_STATE_UNAVAIL &&
zpool_enable_datasets(zhp, mntopts, 0) != 0) {
ret = 1;
- (void) fprintf(stderr, gettext("Split was succssful, but "
+ (void) fprintf(stderr, gettext("Split was successful, but "
"the datasets could not all be mounted\n"));
(void) fprintf(stderr, gettext("Try doing '%s' with a "
"different altroot\n"), "zpool import");
return (ret);
}
+/*
+ * zpool reguid <pool>
+ */
+int
+zpool_do_reguid(int argc, char **argv)
+{
+ int c;
+ char *poolname;
+ zpool_handle_t *zhp;
+ int ret = 0;
+
+ /* check options */
+ while ((c = getopt(argc, argv, "")) != -1) {
+ switch (c) {
+ 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 > 1) {
+ (void) fprintf(stderr, gettext("too many arguments\n"));
+ usage(B_FALSE);
+ }
+
+ poolname = argv[0];
+ if ((zhp = zpool_open(g_zfs, poolname)) == NULL)
+ return (1);
+
+ ret = zpool_reguid(zhp);
+
+ zpool_close(zhp);
+ return (ret);
+}
+
+
+/*
+ * zpool reopen <pool>
+ *
+ * Reopen the pool so that the kernel can update the sizes of all vdevs.
+ *
+ * NOTE: This command is currently undocumented. If the command is ever
+ * exposed then the appropriate usage() messages will need to be made.
+ */
+int
+zpool_do_reopen(int argc, char **argv)
+{
+ int ret = 0;
+ zpool_handle_t *zhp;
+ char *pool;
+
+ argc--;
+ argv++;
+
+ if (argc != 1)
+ return (2);
+
+ pool = argv[0];
+ if ((zhp = zpool_open_canfail(g_zfs, pool)) == NULL)
+ return (1);
+
+ ret = zpool_reopen(zhp);
+ zpool_close(zhp);
+ return (ret);
+}
+
typedef struct scrub_cbdata {
int cb_type;
int cb_argc;
/*
* If the pool was faulted then we may not have been able to
- * obtain the config. Otherwise, if have anything in the dedup
+ * obtain the config. Otherwise, if we have anything in the dedup
* table continue processing the stats.
*/
if (nvlist_lookup_uint64_array(config, ZPOOL_CONFIG_DDT_OBJ_STATS,
* pool: tank
* status: DEGRADED
* reason: One or more devices ...
- * see: http://www.sun.com/msg/ZFS-xxxx-01
+ * see: http://zfsonlinux.org/msg/ZFS-xxxx-01
* config:
* mirror DEGRADED
* c1t0d0 OK
}
if (msgid != NULL)
- (void) printf(gettext(" see: http://www.sun.com/msg/%s\n"),
+ (void) printf(gettext(" see: http://zfsonlinux.org/msg/%s\n"),
msgid);
if (config != NULL) {
status_callback, &cb);
if (argc == 0 && cb.cb_count == 0)
- (void) printf(gettext("no pools available\n"));
+ (void) fprintf(stderr, gettext("no pools available\n"));
else if (cb.cb_explain && cb.cb_first && cb.cb_allpools)
(void) printf(gettext("all pools are healthy\n"));
&cbdata);
if (argc == 0 && cbdata.first == B_TRUE) {
- (void) printf(gettext("no pools available\n"));
+ (void) fprintf(stderr, gettext("no pools available\n"));
return (0);
}
return (ret);
}
+typedef struct ev_opts {
+ int verbose;
+ int scripted;
+ int follow;
+ int clear;
+} ev_opts_t;
+
+static void
+zpool_do_events_short(nvlist_t *nvl)
+{
+ char ctime_str[26], str[32], *ptr;
+ int64_t *tv;
+ uint_t n;
+
+ verify(nvlist_lookup_int64_array(nvl, FM_EREPORT_TIME, &tv, &n) == 0);
+ memset(str, ' ', 32);
+ (void) ctime_r((const time_t *)&tv[0], ctime_str);
+ (void) strncpy(str, ctime_str+4, 6); /* 'Jun 30' */
+ (void) strncpy(str+7, ctime_str+20, 4); /* '1993' */
+ (void) strncpy(str+12, ctime_str+11, 8); /* '21:49:08' */
+ (void) sprintf(str+20, ".%09lld", (longlong_t)tv[1]);/* '.123456789' */
+ (void) printf(gettext("%s "), str);
+
+ verify(nvlist_lookup_string(nvl, FM_CLASS, &ptr) == 0);
+ (void) printf(gettext("%s\n"), ptr);
+}
+
+static void
+zpool_do_events_nvprint(nvlist_t *nvl, int depth)
+{
+ nvpair_t *nvp;
+
+ for (nvp = nvlist_next_nvpair(nvl, NULL);
+ nvp != NULL; nvp = nvlist_next_nvpair(nvl, nvp)) {
+
+ data_type_t type = nvpair_type(nvp);
+ const char *name = nvpair_name(nvp);
+
+ boolean_t b;
+ uint8_t i8;
+ uint16_t i16;
+ uint32_t i32;
+ uint64_t i64;
+ char *str;
+ nvlist_t *cnv;
+
+ printf(gettext("%*s%s = "), depth, "", name);
+
+ switch (type) {
+ case DATA_TYPE_BOOLEAN:
+ printf(gettext("%s"), "1");
+ break;
+
+ case DATA_TYPE_BOOLEAN_VALUE:
+ (void) nvpair_value_boolean_value(nvp, &b);
+ printf(gettext("%s"), b ? "1" : "0");
+ break;
+
+ case DATA_TYPE_BYTE:
+ (void) nvpair_value_byte(nvp, &i8);
+ printf(gettext("0x%x"), i8);
+ break;
+
+ case DATA_TYPE_INT8:
+ (void) nvpair_value_int8(nvp, (void *)&i8);
+ printf(gettext("0x%x"), i8);
+ break;
+
+ case DATA_TYPE_UINT8:
+ (void) nvpair_value_uint8(nvp, &i8);
+ printf(gettext("0x%x"), i8);
+ break;
+
+ case DATA_TYPE_INT16:
+ (void) nvpair_value_int16(nvp, (void *)&i16);
+ printf(gettext("0x%x"), i16);
+ break;
+
+ case DATA_TYPE_UINT16:
+ (void) nvpair_value_uint16(nvp, &i16);
+ printf(gettext("0x%x"), i16);
+ break;
+
+ case DATA_TYPE_INT32:
+ (void) nvpair_value_int32(nvp, (void *)&i32);
+ printf(gettext("0x%x"), i32);
+ break;
+
+ case DATA_TYPE_UINT32:
+ (void) nvpair_value_uint32(nvp, &i32);
+ printf(gettext("0x%x"), i32);
+ break;
+
+ case DATA_TYPE_INT64:
+ (void) nvpair_value_int64(nvp, (void *)&i64);
+ printf(gettext("0x%llx"), (u_longlong_t)i64);
+ break;
+
+ case DATA_TYPE_UINT64:
+ (void) nvpair_value_uint64(nvp, &i64);
+ printf(gettext("0x%llx"), (u_longlong_t)i64);
+ break;
+
+ case DATA_TYPE_HRTIME:
+ (void) nvpair_value_hrtime(nvp, (void *)&i64);
+ printf(gettext("0x%llx"), (u_longlong_t)i64);
+ break;
+
+ case DATA_TYPE_STRING:
+ (void) nvpair_value_string(nvp, &str);
+ printf(gettext("\"%s\""), str ? str : "<NULL>");
+ break;
+
+ case DATA_TYPE_NVLIST:
+ printf(gettext("(embedded nvlist)\n"));
+ (void) nvpair_value_nvlist(nvp, &cnv);
+ zpool_do_events_nvprint(cnv, depth + 8);
+ printf(gettext("%*s(end %s)"), depth, "", name);
+ break;
+
+ case DATA_TYPE_NVLIST_ARRAY: {
+ nvlist_t **val;
+ uint_t i, nelem;
+
+ (void) nvpair_value_nvlist_array(nvp, &val, &nelem);
+ printf(gettext("(%d embedded nvlists)\n"), nelem);
+ for (i = 0; i < nelem; i++) {
+ printf(gettext("%*s%s[%d] = %s\n"),
+ depth, "", name, i, "(embedded nvlist)");
+ zpool_do_events_nvprint(val[i], depth + 8);
+ printf(gettext("%*s(end %s[%i])\n"),
+ depth, "", name, i);
+ }
+ printf(gettext("%*s(end %s)\n"), depth, "", name);
+ }
+ break;
+
+ case DATA_TYPE_INT8_ARRAY: {
+ int8_t *val;
+ uint_t i, nelem;
+
+ (void) nvpair_value_int8_array(nvp, &val, &nelem);
+ for (i = 0; i < nelem; i++)
+ printf(gettext("0x%x "), val[i]);
+
+ break;
+ }
+
+ case DATA_TYPE_UINT8_ARRAY: {
+ uint8_t *val;
+ uint_t i, nelem;
+
+ (void) nvpair_value_uint8_array(nvp, &val, &nelem);
+ for (i = 0; i < nelem; i++)
+ printf(gettext("0x%x "), val[i]);
+
+ break;
+ }
+
+ case DATA_TYPE_INT16_ARRAY: {
+ int16_t *val;
+ uint_t i, nelem;
+
+ (void) nvpair_value_int16_array(nvp, &val, &nelem);
+ for (i = 0; i < nelem; i++)
+ printf(gettext("0x%x "), val[i]);
+
+ break;
+ }
+
+ case DATA_TYPE_UINT16_ARRAY: {
+ uint16_t *val;
+ uint_t i, nelem;
+
+ (void) nvpair_value_uint16_array(nvp, &val, &nelem);
+ for (i = 0; i < nelem; i++)
+ printf(gettext("0x%x "), val[i]);
+
+ break;
+ }
+
+ case DATA_TYPE_INT32_ARRAY: {
+ int32_t *val;
+ uint_t i, nelem;
+
+ (void) nvpair_value_int32_array(nvp, &val, &nelem);
+ for (i = 0; i < nelem; i++)
+ printf(gettext("0x%x "), val[i]);
+
+ break;
+ }
+
+ case DATA_TYPE_UINT32_ARRAY: {
+ uint32_t *val;
+ uint_t i, nelem;
+
+ (void) nvpair_value_uint32_array(nvp, &val, &nelem);
+ for (i = 0; i < nelem; i++)
+ printf(gettext("0x%x "), val[i]);
+
+ break;
+ }
+
+ case DATA_TYPE_INT64_ARRAY: {
+ int64_t *val;
+ uint_t i, nelem;
+
+ (void) nvpair_value_int64_array(nvp, &val, &nelem);
+ for (i = 0; i < nelem; i++)
+ printf(gettext("0x%llx "), (u_longlong_t)val[i]);
+
+ break;
+ }
+
+ case DATA_TYPE_UINT64_ARRAY: {
+ uint64_t *val;
+ uint_t i, nelem;
+
+ (void) nvpair_value_uint64_array(nvp, &val, &nelem);
+ for (i = 0; i < nelem; i++)
+ printf(gettext("0x%llx "), (u_longlong_t)val[i]);
+
+ break;
+ }
+
+ case DATA_TYPE_STRING_ARRAY:
+ case DATA_TYPE_BOOLEAN_ARRAY:
+ case DATA_TYPE_BYTE_ARRAY:
+ case DATA_TYPE_DOUBLE:
+ case DATA_TYPE_UNKNOWN:
+ printf(gettext("<unknown>"));
+ break;
+ }
+
+ printf(gettext("\n"));
+ }
+}
+
+static int
+zpool_do_events_next(ev_opts_t *opts)
+{
+ nvlist_t *nvl;
+ int cleanup_fd, ret, dropped;
+
+ cleanup_fd = open(ZFS_DEV, O_RDWR);
+ VERIFY(cleanup_fd >= 0);
+
+ if (!opts->scripted)
+ (void) printf(gettext("%-30s %s\n"), "TIME", "CLASS");
+
+ while (1) {
+ ret = zpool_events_next(g_zfs, &nvl, &dropped,
+ !!opts->follow, cleanup_fd);
+ if (ret || nvl == NULL)
+ break;
+
+ if (dropped > 0)
+ (void) printf(gettext("dropped %d events\n"), dropped);
+
+ zpool_do_events_short(nvl);
+
+ if (opts->verbose) {
+ zpool_do_events_nvprint(nvl, 8);
+ printf(gettext("\n"));
+ }
+
+ nvlist_free(nvl);
+ }
+
+ VERIFY(0 == close(cleanup_fd));
+
+ return (ret);
+}
+
+static int
+zpool_do_events_clear(ev_opts_t *opts)
+{
+ int count, ret;
+
+ ret = zpool_events_clear(g_zfs, &count);
+ if (!ret)
+ (void) printf(gettext("cleared %d events\n"), count);
+
+ return (ret);
+}
+
+/*
+ * zpool events [-vfc]
+ *
+ * Displays events logs by ZFS.
+ */
+int
+zpool_do_events(int argc, char **argv)
+{
+ ev_opts_t opts = { 0 };
+ int ret;
+ int c;
+
+ /* check options */
+ while ((c = getopt(argc, argv, "vHfc")) != -1) {
+ switch (c) {
+ case 'v':
+ opts.verbose = 1;
+ break;
+ case 'H':
+ opts.scripted = 1;
+ break;
+ case 'f':
+ opts.follow = 1;
+ break;
+ case 'c':
+ opts.clear = 1;
+ break;
+ case '?':
+ (void) fprintf(stderr, gettext("invalid option '%c'\n"),
+ optopt);
+ usage(B_FALSE);
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (opts.clear)
+ ret = zpool_do_events_clear(&opts);
+ else
+ ret = zpool_do_events_next(&opts);
+
+ return ret;
+}
+
static int
get_callback(zpool_handle_t *zhp, void *data)
{
(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;
/*
/*
* Special case '-?'
*/
- if (strcmp(cmdname, "-?") == 0)
+ if ((strcmp(cmdname, "-?") == 0) ||
+ strcmp(cmdname, "--help") == 0)
usage(B_TRUE);
+ if ((g_zfs = libzfs_init()) == NULL)
+ return (1);
+
+ libzfs_print_on_error(g_zfs, B_TRUE);
+
zpool_set_history_str("zpool", argc, argv, history_str);
verify(zpool_stage_history(g_zfs, history_str) == 0);