zpool_main.c revision e45ce728996d8e573eecb27f555fb86aaff0cafd
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <assert.h>
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <libgen.h>
#include <libintl.h>
#include <libuutil.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <priv.h>
#include <pwd.h>
#include <zone.h>
#include <libzfs.h>
#include "zpool_util.h"
static int zpool_do_create(int, char **);
static int zpool_do_destroy(int, char **);
static int zpool_do_add(int, char **);
static int zpool_do_remove(int, char **);
static int zpool_do_list(int, char **);
static int zpool_do_iostat(int, char **);
static int zpool_do_status(int, char **);
static int zpool_do_online(int, char **);
static int zpool_do_offline(int, char **);
static int zpool_do_clear(int, char **);
static int zpool_do_attach(int, char **);
static int zpool_do_detach(int, char **);
static int zpool_do_replace(int, char **);
static int zpool_do_scrub(int, char **);
static int zpool_do_import(int, char **);
static int zpool_do_export(int, char **);
static int zpool_do_upgrade(int, char **);
static int zpool_do_history(int, char **);
static int zpool_do_get(int, char **);
static int zpool_do_set(int, char **);
/*
* These libumem hooks provide a reasonable set of defaults for the allocator's
* debugging facilities.
*/
const char *
_umem_debug_init(void)
{
return ("default,verbose"); /* $UMEM_DEBUG setting */
}
const char *
_umem_logging_init(void)
{
return ("fail,contents"); /* $UMEM_LOGGING setting */
}
typedef enum {
} zpool_help_t;
typedef struct zpool_command {
const char *name;
int (*func)(int, char **);
/*
* Master command table. Each ZFS command has a name, associated function, and
* usage message. The usage messages need to be internationalized, so we have
* to have a function to return the usage message based on a command index.
*
* These commands are organized according to how they are displayed in the usage
* message. An empty command (one with a NULL name) indicates an empty line in
* the generic usage message.
*/
static zpool_command_t command_table[] = {
{ NULL },
{ NULL },
{ NULL },
{ NULL },
{ NULL },
{ NULL },
{ NULL },
};
static const char *
switch (idx) {
case HELP_ADD:
return (gettext("\tadd [-fn] <pool> <vdev> ...\n"));
case HELP_ATTACH:
return (gettext("\tattach [-f] <pool> <device> "
"<new-device>\n"));
case HELP_CLEAR:
return (gettext("\tclear <pool> [device]\n"));
case HELP_CREATE:
return (gettext("\tcreate [-fn] [-R root] [-m mountpoint] "
"<pool> <vdev> ...\n"));
case HELP_DESTROY:
return (gettext("\tdestroy [-f] <pool>\n"));
case HELP_DETACH:
return (gettext("\tdetach <pool> <device>\n"));
case HELP_EXPORT:
return (gettext("\texport [-f] <pool> ...\n"));
case HELP_HISTORY:
return (gettext("\thistory [-il] [<pool>] ...\n"));
case HELP_IMPORT:
return (gettext("\timport [-d dir] [-D]\n"
"\timport [-p property=value] [-d dir] [-D] [-f] "
"[-o opts] [-R root] -a\n"
"\timport [-p property=value] [-d dir] [-D] [-f] \n"
"\t [-o opts] [-R root ] <pool | id> [newpool]\n"));
case HELP_IOSTAT:
return (gettext("\tiostat [-v] [pool] ... [interval "
"[count]]\n"));
case HELP_LIST:
return (gettext("\tlist [-H] [-o field[,...]] [pool] ...\n"));
case HELP_OFFLINE:
return (gettext("\toffline [-t] <pool> <device> ...\n"));
case HELP_ONLINE:
return (gettext("\tonline <pool> <device> ...\n"));
case HELP_REPLACE:
return (gettext("\treplace [-f] <pool> <device> "
"[new-device]\n"));
case HELP_REMOVE:
return (gettext("\tremove <pool> <device>\n"));
case HELP_SCRUB:
return (gettext("\tscrub [-s] <pool> ...\n"));
case HELP_STATUS:
return (gettext("\tstatus [-vx] [pool] ...\n"));
case HELP_UPGRADE:
return (gettext("\tupgrade\n"
"\tupgrade -v\n"
"\tupgrade <-a | pool>\n"));
case HELP_GET:
return (gettext("\tget <\"all\" | property[,...]> "
"<pool> ...\n"));
case HELP_SET:
return (gettext("\tset <property=value> <pool> \n"));
}
abort();
/* NOTREACHED */
}
/*
* Fields available for 'zpool list'.
*/
typedef enum {
#define MAX_FIELDS 10
typedef struct column_def {
const char *cd_title;
enum {
} cd_justify;
} column_def_t;
static column_def_t column_table[] = {
};
static char *column_subopts[] = {
"name",
"size",
"used",
"available",
"capacity",
"health",
"root",
};
/*
* Callback routine that will print out a pool property value.
*/
static zpool_prop_t
{
else
return (ZFS_PROP_CONT);
}
/*
* Display usage message. If we're inside a command, display only the usage for
* that command. Otherwise, iterate over the entire command table and display
* a complete usage message.
*/
void
{
int i;
if (current_command == NULL) {
int i;
gettext("where 'command' is one of the following:\n\n"));
for (i = 0; i < NCOMMAND; i++) {
else
}
} else {
"of the following:\n\n"));
for (i = 0; column_subopts[i] != NULL; i++)
}
}
if (current_command != NULL &&
gettext("\nthe following properties are supported:\n"));
"PROPERTY", "VALUES");
/* Iterate over all properties */
}
/*
* See comments at end of main().
*/
(void) printf("dumping core by request\n");
abort();
}
}
const char *
state_to_health(int vs_state)
{
switch (vs_state) {
case VDEV_STATE_CLOSED:
case VDEV_STATE_CANT_OPEN:
case VDEV_STATE_OFFLINE:
case VDEV_STATE_DEGRADED:
case VDEV_STATE_HEALTHY:
}
}
const char *
{
case VDEV_STATE_CLOSED:
case VDEV_STATE_CANT_OPEN:
return (gettext("FAULTED"));
else
return (gettext("UNAVAIL"));
case VDEV_STATE_OFFLINE:
return (gettext("OFFLINE"));
case VDEV_STATE_REMOVED:
return (gettext("REMOVED"));
case VDEV_STATE_FAULTED:
return (gettext("FAULTED"));
case VDEV_STATE_DEGRADED:
return (gettext("DEGRADED"));
case VDEV_STATE_HEALTHY:
return (gettext("ONLINE"));
}
return (gettext("UNKNOWN"));
}
void
{
char *vname;
return;
for (c = 0; c < children; c++) {
&is_log);
continue;
B_FALSE);
}
}
/*
* zpool add [-fn] <pool> <vdev> ...
*
* -f Force addition of devices, even if they appear in use
* -n Do not add the devices, but display the resulting layout if
* they were to be added.
*
* Adds the given vdevs to 'pool'. As with create, the bulk of this work is
* handled by get_vdev_spec(), which constructs the nvlist needed to pass to
* libzfs.
*/
int
{
int c;
char *poolname;
int ret;
/* check options */
switch (c) {
case 'f':
break;
case 'n':
break;
case '?':
optopt);
}
}
/* get pool name and check number of arguments */
if (argc < 1) {
}
if (argc < 2) {
}
argc--;
argv++;
return (1);
poolname);
return (1);
}
/* pass off to get_vdev_spec for processing */
return (1);
}
if (dryrun) {
&poolnvroot) == 0);
/* print original main pool and new tree */
/* Do the same for the logs */
if (num_logs(poolnvroot) > 0) {
}
ret = 0;
} else {
}
return (ret);
}
/*
* zpool remove <pool> <vdev>
*
* Removes the given vdev from the pool. Currently, this only supports removing
* spares from the pool. Eventually, we'll want to support removing leaf vdevs
* (as an alias for 'detach') as well as toplevel vdevs.
*/
int
{
char *poolname;
int ret;
argc--;
argv++;
/* get pool name and check number of arguments */
if (argc < 1) {
}
if (argc < 2) {
}
return (1);
return (ret);
}
/*
* zpool create [-fn] [-R root] [-m mountpoint] <pool> <dev> ...
*
* -f Force creation, even if devices appear in use
* -n Do not create the pool, but display the resulting layout if it
* were to be created.
* -R Create a pool under an alternate root
* -m Set default mountpoint for the root dataset. By default it's
* '/<pool>'
*
* Creates the named pool according to the given vdev specification. The
* bulk of the vdev processing is done in get_vdev_spec() in zpool_vdev.c. Once
* we get the nvlist back from get_vdev_spec(), we either print out the contents
* (if '-n' was specified), or pass it to libzfs to do the creation.
*/
int
{
int c;
char *poolname;
int ret;
char *mountpoint = NULL;
/* check options */
switch (c) {
case 'f':
break;
case 'n':
break;
case 'R':
break;
case 'm':
mountpoint = optarg;
break;
case ':':
"'%c' option\n"), optopt);
break;
case '?':
optopt);
}
}
/* get pool name and check number of arguments */
if (argc < 1) {
}
if (argc < 2) {
}
/*
* As a special case, check for use of '/' in the name, and direct the
* user to use 'zfs create' instead.
*/
"character '/' in pool name\n"), poolname);
"create a dataset\n"));
return (1);
}
/* pass off to get_vdev_spec for bulk processing */
argv + 1);
return (1);
/* make_root_vdev() allows 0 toplevel children if there are spares */
if (children == 0) {
"specification: at least one toplevel vdev must be "
"specified\n"));
return (1);
}
"must be an absolute path\n"), altroot);
return (1);
}
/*
* Check the validity of the mountpoint and direct the user to use the
* '-m' mountpoint option if it looks like its in use.
*/
if (mountpoint == NULL ||
char buf[MAXPATHLEN];
"'%s': must be an absolute path, 'legacy', or "
"'none'\n"), mountpoint);
return (1);
}
if (mountpoint == NULL) {
else
poolname);
} else {
else
}
if (mountpoint == NULL)
"mountpoint '%s' exists and is not "
"empty\n"), buf);
else
"'%s' exists and is not empty\n"), buf);
"option to provide a different default\n"));
return (1);
}
}
if (dryrun) {
/*
* For a dry run invocation, print out a basic message and run
* through all the vdevs in the list and print out in an
* appropriate hierarchy.
*/
"following layout:\n\n"), poolname);
ret = 0;
} else {
ret = 1;
/*
* Hand off to libzfs.
*/
if (mountpoint != NULL)
mountpoint) == 0);
}
"been omitted\n"));
}
}
return (ret);
}
/*
* zpool destroy <pool>
*
* -f Forcefully unmount any datasets
*
* Destroy the given pool. Automatically unmounts any datasets in the pool.
*/
int
{
int c;
char *pool;
int ret;
/* check options */
switch (c) {
case 'f':
break;
case '?':
optopt);
}
}
/* check arguments */
if (argc < 1) {
}
if (argc > 1) {
}
/*
* As a special case, check for use of '/' in the name, and
* direct the user to use 'zfs destroy' instead.
*/
"destroy a dataset\n"));
return (1);
}
return (1);
}
return (ret);
}
/*
* zpool export [-f] <pool> ...
*
* -f Forcefully unmount datasets
*
* Export the given pools. By default, the command will attempt to cleanly
* unmount any active datasets within the pool. If the '-f' flag is specified,
* then the datasets will be forcefully unmounted.
*/
int
{
int c;
int ret;
int i;
/* check options */
switch (c) {
case 'f':
break;
case '?':
optopt);
}
}
/* check arguments */
if (argc < 1) {
}
ret = 0;
for (i = 0; i < argc; i++) {
ret = 1;
continue;
}
ret = 1;
continue;
}
if (zpool_export(zhp) != 0)
ret = 1;
}
return (ret);
}
/*
* Given a vdev configuration, determine the maximum width needed for the device
* name column.
*/
static int
{
int ret;
for (c = 0; c < children; c++)
}
for (c = 0; c < children; c++)
}
return (max);
}
/*
* Print the configuration of an exported pool. Iterate over all vdevs in the
* pool, printing out the name and status for each one.
*/
void
{
return;
(void) printf(" ");
case VDEV_AUX_OPEN_FAILED:
break;
case VDEV_AUX_BAD_GUID_SUM:
break;
case VDEV_AUX_NO_REPLICAS:
break;
case VDEV_AUX_VERSION_NEWER:
break;
case VDEV_AUX_ERR_EXCEEDED:
break;
default:
break;
}
}
(void) printf("\n");
return;
for (c = 0; c < children; c++) {
&is_log);
continue;
}
return;
for (c = 0; c < children; c++) {
}
}
/*
* Display the status for the given pool.
*/
static void
{
char *name;
char *msgid;
int reason;
const char *health;
int namewidth;
&name) == 0);
&guid) == 0);
&pool_state) == 0);
&nvroot) == 0);
if (pool_state == POOL_STATE_DESTROYED)
(void) printf("\n");
switch (reason) {
"from the system.\n"));
break;
"corrupted data.\n"));
break;
break;
case ZPOOL_STATUS_OFFLINE_DEV:
"are offlined.\n"));
break;
"corrupted.\n"));
break;
"older on-disk version.\n"));
break;
"incompatible version.\n"));
break;
"another system.\n"));
break;
"faulted.\n"));
break;
default:
/*
* No other status can be seen when importing pools.
*/
}
/*
* Print out an action according to the overall state of the pool.
*/
if (reason == ZPOOL_STATUS_VERSION_OLDER)
"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)
"imported using its name or numeric "
"identifier and\n\tthe '-f' flag.\n"));
else
"imported using its name or numeric "
"identifier.\n"));
"despite missing or damaged devices. The\n\tfault "
"tolerance of the pool may be compromised if imported.\n"));
} else {
switch (reason) {
"imported. Access the pool on a system running "
"newer\n\tsoftware, or recreate the pool from "
"backup.\n"));
break;
"imported. Attach the missing\n\tdevices and try "
"again.\n"));
break;
default:
"imported due to damaged devices or data.\n"));
}
}
/*
* If the state is "closed" or "can't open", and the aux state
* is "corrupt data":
*/
if (pool_state == POOL_STATE_DESTROYED)
"but can be imported using the '-Df' flags.\n"));
else if (pool_state != POOL_STATE_EXPORTED)
"on another system, but can be imported using\n\t"
"the '-f' flag.\n"));
}
msgid);
if (namewidth < 10)
namewidth = 10;
}
if (reason == ZPOOL_STATUS_BAD_GUID_SUM) {
"be part of this pool, though their\n\texact "
"configuration cannot be determined.\n"));
}
}
/*
* Perform the import for the given configuration. This passes the heavy
* lifting off to zpool_import(), and then mounts the datasets contained within
* the pool.
*/
static int
{
char *name;
int error = 0;
&name) == 0);
ZPOOL_CONFIG_POOL_STATE, &state) == 0);
ZPOOL_CONFIG_VERSION, &version) == 0);
if (version > SPA_VERSION) {
"is formatted using a newer ZFS version\n"), name);
return (1);
&hostid) == 0) {
char *hostname;
time_t t;
ZPOOL_CONFIG_HOSTNAME, &hostname) == 0);
ZPOOL_CONFIG_TIMESTAMP, ×tamp) == 0);
t = timestamp;
"'%s': pool may be in use from other "
"system, it was last accessed by %s "
(unsigned long)hostid,
"import anyway\n"));
return (1);
}
} else {
"pool may be in use from other system\n"), name);
"anyway\n"));
return (1);
}
}
return (1);
if (props) {
char *value;
do {
&value)) == 0);
break;
}
}
return (1);
}
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]
*
* one directory can be specified using multiple '-d' options.
*
* -D Scan for previously destroyed pools or import all or only
* specified destroyed pools.
*
* -R Temporarily import the pool, with all mountpoints relative to
* the given root. The pool will remain exported when the machine
* is rebooted.
*
* -f Force import, even if it appears that the pool is active.
*
* -a Import all pools found.
*
* -o temporary mount options.
*
* -p property=value
*
* The import command scans for pools to import, and import pools based on pool
* name and GUID. The pool can also be renamed as part of the import process.
*/
int
{
char **searchdirs = NULL;
int nsearch = 0;
int c;
int err;
char *searchname;
char *propname;
/* check options */
switch (c) {
case 'a':
break;
case 'd':
if (searchdirs == NULL) {
searchdirs = safe_malloc(sizeof (char *));
} else {
sizeof (char *));
sizeof (char *));
searchdirs = tmp;
}
break;
case 'D':
break;
case 'f':
break;
case 'o':
break;
case 'R':
break;
case ':':
"'%c' option\n"), optopt);
break;
case 'p':
gettext("internal error: "
"out of memory\n"));
goto error;
}
"'=' for -o option\n"));
goto error;
}
*propval = '\0';
propval++;
gettext("property '%s' is "
"not a valid pool property\n"), propname);
goto error;
}
&strval) == 0) {
"specified multiple times\n"), propname);
goto error;
}
"error: out of memory\n"));
goto error;
}
break;
case '?':
optopt);
}
}
if (searchdirs == NULL) {
searchdirs = safe_malloc(sizeof (char *));
searchdirs[0] = "/dev/dsk";
nsearch = 1;
}
/* check argument count */
if (do_all) {
if (argc != 0) {
}
} else {
if (argc > 2) {
}
/*
* Check for the SYS_CONFIG privilege. We do this explicitly
* here because otherwise any attempt to discover pools will
* silently fail.
*/
"discover pools: permission denied\n"));
return (1);
}
}
return (1);
}
/*
* We now have a list of all available pools in the given directories.
* Depending on the arguments given, we do one of the following:
*
* <none> Iterate through all pools and display information about
* each one.
*
* -a Iterate through all pools and try to import each one.
*
* name and import that one.
*
* -D Above options applies only to destroyed pools.
*/
if (argc != 0) {
char *endptr;
errno = 0;
searchname = argv[0];
else
searchname = NULL;
found_config = NULL;
}
err = 0;
&pool_state) == 0);
continue;
continue;
if (argc == 0) {
if (first)
else if (!do_all)
(void) printf("\n");
if (do_all)
else
} else if (searchname != NULL) {
char *name;
/*
* We are searching for a pool based on name.
*/
ZPOOL_CONFIG_POOL_NAME, &name) == 0);
if (found_config != NULL) {
"cannot import '%s': more than "
"one matching pool\n"), searchname);
"import by numeric ID instead\n"));
}
}
} else {
/*
* Search for a pool by guid.
*/
ZPOOL_CONFIG_POOL_GUID, &guid) == 0);
if (guid == searchguid)
}
}
/*
* If we were searching for a specific pool, verify that we found a
* pool, and then do the import.
*/
if (found_config == NULL) {
"no such pool available\n"), argv[0]);
} else {
}
}
/*
* If we were just looking for pools, report an error if none were
* found.
*/
gettext("no pools available to import\n"));
if (props)
return (err ? 1 : 0);
}
typedef struct iostat_cbdata {
int cb_verbose;
int cb_iteration;
int cb_namewidth;
static void
{
int i = 0;
for (i = 0; i < cb->cb_namewidth; i++)
(void) printf("-");
(void) printf(" ----- ----- ----- ----- ----- -----\n");
}
static void
{
(void) printf("%*s capacity operations bandwidth\n",
(void) printf("%-*s used avail read write read write\n",
}
/*
* Display a single statistic.
*/
void
{
char buf[64];
}
/*
* Print out all the statistics for the given vdev. This can either be the
* toplevel configuration, or called recursively. If 'name' is NULL, then this
* is a verbose output, and we don't want to display the toplevel pool stats.
*/
void
{
vdev_stat_t zerovs = { 0 };
double scale;
char *vname;
} else {
}
else
if (tdelta == 0)
scale = 1.0;
else
/* only toplevel vdevs have capacity stats */
(void) printf(" - -");
} else {
}
(void) printf("\n");
if (!cb->cb_verbose)
return;
return;
&oldchild, &c) != 0)
return;
for (c = 0; c < children; c++) {
}
}
static int
{
/*
* If the pool has disappeared, remove it from the list and continue.
*/
return (-1);
if (missing)
return (0);
}
/*
* Callback to print out the iostats for the given pool.
*/
int
{
&newnvroot) == 0);
else
&oldnvroot) == 0);
/*
* Print out the statistics for the pool.
*/
if (cb->cb_verbose)
return (0);
}
int
{
&nvroot) == 0);
if (!cb->cb_verbose)
else
}
/*
* The width must fall into the range [10,38]. The upper limit is the
* maximum we can have and still fit in 80 columns.
*/
return (0);
}
/*
* zpool iostat [-v] [pool] ... [interval [count]]
*
* -v Display statistics for individual vdevs
*
* This command can be tricky because we want to be able to deal with pool
* creation/destruction as well as vdev configuration changes. The bulk of this
* processing is handled by the pool_list_* routines in zpool_iter.c. We rely
* on pool_list_update() to detect the addition of new pools. Configuration
* changes are all handled within libzfs.
*/
int
{
int c;
int ret;
int npools;
/* check options */
switch (c) {
case 'v':
break;
case '?':
optopt);
}
}
/*
* Determine if the last argument is an integer or a pool name
*/
char *end;
errno = 0;
if (interval == 0) {
"cannot be zero\n"));
}
/*
* Ignore the last parameter
*/
argc--;
} else {
/*
* If this is not a valid number, just plow on. The
* user will get a more informative error message later
* on.
*/
interval = 0;
}
}
/*
* If the last argument is also an integer, then we have both a count
* and an integer.
*/
char *end;
errno = 0;
if (interval == 0) {
"cannot be zero\n"));
}
/*
* Ignore the last parameter
*/
argc--;
} else {
interval = 0;
}
}
/*
* Construct the list of all interesting pools.
*/
ret = 0;
return (1);
return (1);
}
return (1);
}
/*
* Enter the main iostat loop.
*/
cb.cb_iteration = 0;
cb.cb_namewidth = 0;
for (;;) {
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.
*/
/*
* Iterate over all pools to determine the maximum width
* for the pool / device name column across all pools.
*/
cb.cb_namewidth = 0;
/*
* If it's the first time, or verbose mode, print the header.
*/
/*
* 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 (verbose)
(void) printf("\n");
/*
* Flush the output so that redirection to a file isn't buffered
* indefinitely.
*/
if (interval == 0)
break;
break;
}
return (ret);
}
typedef struct list_cbdata {
int cb_fields[MAX_FIELDS];
int cb_fieldcount;
/*
* Given a list of columns to display, output appropriate headers for each one.
*/
void
{
int i;
const char *fmt;
for (i = 0; i < count; i++) {
if (i != 0)
(void) printf(" ");
fmt = "%-*s";
else
fmt = "%*s";
}
(void) printf("\n");
}
int
{
int i;
char buf[ZPOOL_MAXNAMELEN];
const char *fmt;
if (!cbp->cb_scripted)
}
} else {
}
for (i = 0; i < cbp->cb_fieldcount; i++) {
if (i != 0) {
if (cbp->cb_scripted)
(void) printf("\t");
else
(void) printf(" ");
}
case ZPOOL_FIELD_NAME:
break;
case ZPOOL_FIELD_SIZE:
else
break;
case ZPOOL_FIELD_USED:
else
break;
case ZPOOL_FIELD_AVAILABLE:
else
break;
case ZPOOL_FIELD_CAPACITY:
} else {
}
break;
case ZPOOL_FIELD_HEALTH:
} else {
ZPOOL_CONFIG_VDEV_TREE, &nvroot) == 0);
&vsc) == 0);
sizeof (buf));
}
break;
case ZPOOL_FIELD_ROOT:
break;
}
if (cbp->cb_scripted)
else {
fmt = "%-*s";
else
fmt = "%*s";
}
}
(void) printf("\n");
return (0);
}
/*
* zpool list [-H] [-o field[,field]*] [pool] ...
*
* -H Scripted mode. Don't display headers, and separate fields by
* a single tab.
* -o List of fields to display. Defaults to all fields, or
* "name,size,used,available,capacity,health,root"
*
* List all pools in the system, whether or not they're healthy. Output space
* statistics for each one, as well as health status summary.
*/
int
{
int c;
int ret;
list_cbdata_t cb = { 0 };
static char default_fields[] =
"name,size,used,available,capacity,health,root";
char *fields = default_fields;
char *value;
/* check options */
switch (c) {
case 'H':
break;
case 'o':
break;
case ':':
"'%c' option\n"), optopt);
break;
case '?':
optopt);
}
}
while (*fields != '\0') {
"properties given to -o option\n"));
}
"'%s'\n"), value);
}
cb.cb_fieldcount++;
}
return (0);
}
return (ret);
}
static nvlist_t *
{
char *path;
name += 9;
path += 9;
return (nv);
return (NULL);
}
for (c = 0; c < children; c++)
return (match);
return (NULL);
}
static int
{
int c;
int ret;
/* check options */
switch (c) {
case 'f':
break;
case '?':
optopt);
}
}
/* get pool name and check number of arguments */
if (argc < 1) {
}
if (argc < 2) {
gettext("missing <device> specification\n"));
}
if (argc < 3) {
if (!replacing) {
gettext("missing <new_device> specification\n"));
}
argc -= 1;
argv += 1;
} else {
argc -= 2;
argv += 2;
}
if (argc > 1) {
}
return (1);
poolname);
return (1);
}
return (1);
}
return (ret);
}
/*
* zpool replace [-f] <pool> <device> <new_device>
*
* -f Force attach, even if <new_device> appears to be in use.
*
* Replace <device> with <new_device>.
*/
/* ARGSUSED */
int
{
}
/*
* zpool attach [-f] <pool> <device> <new_device>
*
* -f Force attach, even if <new_device> appears to be in use.
*
* Attach <new_device> to the mirror containing <device>. If <device> is not
* part of a mirror, then <device> will be transformed into a mirror of
* <device> and <new_device>. In either case, <new_device> will begin life
* with a DTL of [0, now], and will immediately begin to resilver itself.
*/
int
{
}
/*
* zpool detach [-f] <pool> <device>
*
* -f Force detach of <device>, even if DTLs argue against it
* (not supported yet)
*
* Detach a device from a mirror. The operation will be refused if <device>
* is the last device in the mirror, or if the DTLs indicate that this device
* has the only valid copy of some data.
*/
/* ARGSUSED */
int
{
int c;
int ret;
/* check options */
switch (c) {
case 'f':
case '?':
optopt);
}
}
/* get pool name and check number of arguments */
if (argc < 1) {
}
if (argc < 2) {
gettext("missing <device> specification\n"));
}
return (1);
return (ret);
}
/*
* zpool online <pool> <device> ...
*/
int
{
int c, i;
char *poolname;
int ret = 0;
/* check options */
switch (c) {
case 't':
case '?':
optopt);
}
}
/* get pool name and check number of arguments */
if (argc < 1) {
}
if (argc < 2) {
}
return (1);
for (i = 1; i < argc; i++) {
if (newstate != VDEV_STATE_HEALTHY) {
"onlined, but remains in faulted state\n"),
argv[i]);
if (newstate == VDEV_STATE_FAULTED)
"clear' to restore a faulted "
"device\n"));
else
"replace' to replace devices "
"that are no longer present\n"));
}
} else {
ret = 1;
}
}
return (ret);
}
/*
* zpool offline [-ft] <pool> <device> ...
*
* -f Force the device into the offline state, even if doing
* so would appear to compromise pool availability.
* (not supported yet)
*
* -t Only take the device off-line temporarily. The offline
* state will not be persistent across reboots.
*/
/* ARGSUSED */
int
{
int c, i;
char *poolname;
int ret = 0;
/* check options */
switch (c) {
case 't':
break;
case 'f':
case '?':
optopt);
}
}
/* get pool name and check number of arguments */
if (argc < 1) {
}
if (argc < 2) {
}
return (1);
for (i = 1; i < argc; i++) {
ret = 1;
}
return (ret);
}
/*
* zpool clear <pool> [device]
*
* Clear all errors associated with a pool or a particular device.
*/
int
{
int ret = 0;
if (argc < 2) {
}
if (argc > 3) {
}
return (1);
ret = 1;
return (ret);
}
typedef struct scrub_cbdata {
int cb_type;
int cb_argc;
char **cb_argv;
int
{
int err;
/*
* Ignore faulted pools.
*/
return (1);
}
return (err != 0);
}
/*
* zpool scrub [-s] <pool> ...
*
* -s Stop. Stops any in-progress scrub.
*/
int
{
int c;
/* check options */
switch (c) {
case 's':
break;
case '?':
optopt);
}
}
if (argc < 1) {
}
}
typedef struct status_cbdata {
int cb_count;
/*
* Print out detailed scrub status.
*/
void
{
double fraction_done;
char *scrub_type;
/*
* If there's never been a scrub, there's not much to say.
*/
return;
}
"resilver" : "scrub";
if (end != 0) {
return;
}
if (examined == 0)
examined = 1;
}
typedef struct spare_cbdata {
static boolean_t
{
return (B_TRUE);
for (c = 0; c < children; c++)
return (B_TRUE);
}
return (B_FALSE);
}
static int
{
&nvroot) == 0);
return (1);
}
return (0);
}
/*
* Print out configuration state as requested by status_callback.
*/
void
{
char *vname;
const char *state;
children = 0;
if (isspare) {
/*
* For hot spares, we use the terms 'INUSE' and 'AVAILABLE' for
* online drives.
*/
state = "INUSE";
state = "AVAIL";
}
if (!isspare) {
}
¬present) == 0) {
char *path;
(void) printf(" ");
case VDEV_AUX_OPEN_FAILED:
break;
case VDEV_AUX_BAD_GUID_SUM:
break;
case VDEV_AUX_NO_REPLICAS:
break;
case VDEV_AUX_VERSION_NEWER:
break;
case VDEV_AUX_SPARED:
zpool_get_name(zhp)) == 0)
"use"));
else
"pool '%s'"),
} else {
}
break;
case VDEV_AUX_ERR_EXCEEDED:
break;
default:
break;
}
/*
* Report bytes resilvered/repaired on leaf devices.
*/
"resilvered" : "repaired");
}
(void) printf("\n");
for (c = 0; c < children; c++) {
&is_log);
continue;
}
}
static void
{
char *pathname;
(void) printf("errors: List of errors unavailable "
"(insufficient privileges)\n");
return;
}
(void) printf("errors: Permanent errors have been "
"detected in the following files:\n\n");
&dsobj) == 0);
&obj) == 0);
}
}
static void
int namewidth)
{
uint_t i;
char *name;
if (nspares == 0)
return;
for (i = 0; i < nspares; i++) {
}
}
/*
* Display a summary of pool status. Displays a summary such as:
*
* pool: tank
* status: DEGRADED
* reason: One or more devices ...
* config:
* mirror DEGRADED
* c1t0d0 OK
* c2t0d0 UNAVAIL
*
* When given the '-v' option, we print out the complete config. If the '-e'
* option is specified, then we print out error rate information as well.
*/
int
{
char *msgid;
int reason;
const char *health;
uint_t c;
/*
* If we were given 'zpool status -x', only report those pools with
* problems.
*/
if (!cbp->cb_allpools) {
}
return (0);
}
else
(void) printf("\n");
&nvroot) == 0);
switch (reason) {
"be opened. Sufficient replicas exist for\n\tthe pool to "
"continue functioning in a degraded state.\n"));
"online it using 'zpool online'.\n"));
break;
"be opened. There are insufficient\n\treplicas for the "
"pool to continue functioning.\n"));
"online it using 'zpool online'.\n"));
break;
"be used because the label is missing or\n\tinvalid. "
"Sufficient replicas exist for the pool to continue\n\t"
"functioning in a degraded state.\n"));
"'zpool replace'.\n"));
break;
"be used because the label is missing \n\tor invalid. "
"There are insufficient replicas for the pool to "
"continue\n\tfunctioning.\n"));
"from a backup source.\n"));
break;
case ZPOOL_STATUS_FAILING_DEV:
"experienced an unrecoverable error. An\n\tattempt was "
"made to correct the error. Applications are "
"unaffected.\n"));
"to be replaced, and clear the errors\n\tusing "
"'zpool clear' or replace the device with 'zpool "
"replace'.\n"));
break;
case ZPOOL_STATUS_OFFLINE_DEV:
"been taken offline by the administrator.\n\tSufficient "
"replicas exist for the pool to continue functioning in "
"a\n\tdegraded state.\n"));
"'zpool online' or replace the device with\n\t'zpool "
"replace'.\n"));
break;
case ZPOOL_STATUS_RESILVERING:
"currently being resilvered. The pool will\n\tcontinue "
"to function, possibly in a degraded state.\n"));
"complete.\n"));
break;
"experienced an error resulting in data\n\tcorruption. "
"Applications may be affected.\n"));
"if possible. Otherwise restore the\n\tentire pool from "
"backup.\n"));
break;
"and the pool cannot be opened.\n"));
"from a backup source.\n"));
break;
"older on-disk format. The pool can\n\tstill be used, but "
"some features are unavailable.\n"));
"upgrade'. Once this is done, the\n\tpool will no longer "
"be accessible on older software versions.\n"));
break;
"newer, incompatible on-disk version.\n\tThe pool cannot "
"be accessed on this system.\n"));
"running more recent software, or\n\trestore the pool from "
"backup.\n"));
break;
"faulted in response to persistent errors.\n\tSufficient "
"replicas exist for the pool to continue functioning "
"in a\n\tdegraded state.\n"));
"or use 'zpool clear' to mark the device\n\trepaired.\n"));
break;
"faulted in response to persistent errors. There are "
"insufficient replicas for the pool to\n\tcontinue "
"functioning.\n"));
"from a backup source. Manually marking the device\n"
"\trepaired using 'zpool clear' may allow some data "
"to be recovered.\n"));
break;
default:
/*
* The remaining errors can't actually be generated, yet.
*/
}
msgid);
int namewidth;
if (namewidth < 10)
namewidth = 10;
"NAME", "STATE", "READ", "WRITE", "CKSUM");
&nerr) == 0) {
/*
* If the approximate error count is small, get a
* precise count by fetching the entire log and
* uniquifying the results.
*/
nerr = 0;
nerr++;
}
}
(void) printf("\n");
if (nerr == 0)
"errors\n"));
else if (!cbp->cb_verbose)
"errors, use '-v' for a list\n"),
(u_longlong_t)nerr);
else
}
} else {
"determined.\n"));
}
return (0);
}
/*
* zpool status [-vx] [pool] ...
*
* -v Display complete error logs
* -x Display only pools with potential problems
*
* Describes the health status of all pools or some subset.
*/
int
{
int c;
int ret;
status_cbdata_t cb = { 0 };
/* check options */
switch (c) {
case 'v':
break;
case 'x':
break;
case '?':
optopt);
}
}
if (argc == 0)
return (ret);
}
typedef struct upgrade_cbdata {
int cb_all;
int cb_first;
int cb_newer;
int cb_argc;
char **cb_argv;
static int
{
int ret = 0;
&version) == 0);
"out of date, and can be upgraded. After "
"being\nupgraded, these pools will no "
"longer be accessible by older software "
"versions.\n\n"));
}
} else {
if (!ret) {
}
}
"formatted using a newer software version and\n"
"cannot be accessed on the current system.\n\n"));
}
}
return (ret);
}
/* ARGSUSED */
static int
{
int ret;
&version) == 0);
"Pool 'log' must be renamed using export and import"
" to upgrade.\n"));
return (1);
}
if (version == SPA_VERSION) {
return (0);
}
if (!ret) {
}
return (ret != 0);
}
/*
* zpool upgrade
* zpool upgrade -v
* zpool upgrade <-a | pool>
*
* With no arguments, display downrev'd ZFS pool available for upgrade.
* Individual pools can be upgraded by specifying the pool, and '-a' will
* upgrade all pools.
*/
int
{
int c;
upgrade_cbdata_t cb = { 0 };
int ret = 0;
/* check options */
switch (c) {
case 'a':
break;
case 'v':
break;
case '?':
optopt);
}
}
if (showversions) {
"incompatible with other arguments\n"));
}
if (argc != 0) {
"incompatible with other arguments\n"));
}
}
"ZFS pool version %llu.\n\n"), SPA_VERSION);
if (showversions) {
"supported:\n\n"));
(void) printf("--- -----------------------------------------"
"---------------\n");
"(replicated metadata)\n"));
"RAID-Z\n"));
"algorithm\n"));
"version, including supported releases, see:\n\n"));
"version/N\n\n");
} else if (argc == 0) {
int notfound;
(void) printf("\n");
(void) printf("\n");
}
}
if (ret == 0) {
if (notfound)
"using this version.\n"));
"for a list of available versions and "
"their associated\nfeatures.\n"));
}
} else {
upgrade_one, &cb);
}
return (ret);
}
typedef struct hist_cbdata {
int longfmt;
int internal;
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",
};
/*
* Print out the command history for a specific pool.
*/
static int
{
char *cmdstr;
char *pathstr;
struct tm t;
char tbuf[30];
int ret, i;
char *hostname;
char *zonename;
char internalstr[MAXPATHLEN];
return (ret);
&records, &numrecords) == 0);
for (i = 0; i < numrecords; i++) {
&dst_time) != 0)
continue;
/* is it an internal event or a standard event? */
&cmdstr) != 0) {
continue;
if (nvlist_lookup_uint64(records[i],
ZPOOL_HIST_INT_EVENT, &ievent) != 0)
continue;
ZPOOL_HIST_TXG, &txg) == 0);
ZPOOL_HIST_INT_STR, &pathstr) == 0);
continue;
(void) snprintf(internalstr,
sizeof (internalstr),
"[internal %s txg:%lld] %s",
pathstr);
}
(void) localtime_r(&tsec, &t);
(void) printf("\n");
continue;
}
(void) printf(" [");
if (nvlist_lookup_uint64(records[i],
ZPOOL_HIST_WHO, &who) == 0) {
if (pwd)
(void) printf("user %s on",
else
(void) printf("user %d on",
(int)who);
} else {
continue;
}
if (nvlist_lookup_string(records[i],
ZPOOL_HIST_HOST, &hostname) == 0) {
}
if (nvlist_lookup_string(records[i],
ZPOOL_HIST_ZONE, &zonename) == 0) {
}
(void) printf("]");
(void) printf("\n");
}
(void) printf("\n");
return (ret);
}
/*
* zpool history <pool>
*
* Displays the history of commands that modified pools.
*/
int
{
hist_cbdata_t cbdata = { 0 };
int ret;
int c;
/* check options */
switch (c) {
case 'l':
break;
case 'i':
break;
case '?':
optopt);
}
}
&cbdata);
return (0);
}
return (ret);
}
static int
{
char value[MAXNAMELEN];
/*
* Skip the special fake placeholder.
*/
continue;
continue;
}
return (0);
}
int
{
libzfs_get_cbdata_t cb = { 0 };
zpool_proplist_t fake_name = { 0 };
int ret;
if (argc < 3)
}
get_callback, &cb);
else
return (ret);
}
typedef struct set_cbdata {
char *cb_propname;
char *cb_value;
} set_cbdata_t;
int
{
int error;
if (!error)
return (error);
}
int
{
set_cbdata_t cb = { 0 };
int error;
}
if (argc < 2) {
"argument\n"));
}
if (argc < 3) {
}
if (argc > 3) {
}
"property=value argument\n"));
}
set_callback, &cb);
return (error);
}
static int
{
int i;
for (i = 0; i < NCOMMAND; i++) {
continue;
*idx = i;
return (0);
}
}
return (1);
}
int
{
int ret;
int i;
char *cmdname;
(void) textdomain(TEXT_DOMAIN);
"initialize ZFS library\n"));
return (1);
}
opterr = 0;
/*
* Make sure the user has specified some command.
*/
if (argc < 2) {
}
/* Handle special case of pool create for staging history */
else
/*
* Special case '-?'
*/
/*
* Run the appropriate command.
*/
if (find_command_idx(cmdname, &i) == 0) {
current_command = &command_table[i];
current_command = &command_table[i];
/*
* 'freeze' is a vile debugging abomination, so we treat
* it as such.
*/
char buf[16384];
} else {
"command '%s'\n"), cmdname);
}
/*
* The 'ZFS_ABORT' environment variable causes us to dump core on exit
* for the purposes of running ::findleaks.
*/
(void) printf("dumping core by request\n");
abort();
}
return (ret);
}