zpool_main.c revision 168498
1317455Sdim/* 2317455Sdim * CDDL HEADER START 3353358Sdim * 4353358Sdim * The contents of this file are subject to the terms of the 5353358Sdim * Common Development and Distribution License (the "License"). 6317455Sdim * You may not use this file except in compliance with the License. 7317455Sdim * 8317455Sdim * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9317455Sdim * or http://www.opensolaris.org/os/licensing. 10353358Sdim * See the License for the specific language governing permissions 11317455Sdim * and limitations under the License. 12317455Sdim * 13320572Sdim * When distributing Covered Code, include this CDDL HEADER in each 14317455Sdim * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15320572Sdim * If applicable, add the following below this CDDL HEADER, with the 16317455Sdim * fields enclosed by brackets "[]" replaced with your own identifying 17353358Sdim * information: Portions Copyright [yyyy] [name of copyright owner] 18353358Sdim * 19317455Sdim * CDDL HEADER END 20317455Sdim */ 21317455Sdim 22317455Sdim/* 23353358Sdim * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 24353358Sdim * Use is subject to license terms. 25353358Sdim */ 26317455Sdim 27317455Sdim#pragma ident "%Z%%M% %I% %E% SMI" 28317455Sdim 29353358Sdim#include <solaris.h> 30353358Sdim#include <assert.h> 31317455Sdim#include <ctype.h> 32317455Sdim#include <dirent.h> 33317455Sdim#include <errno.h> 34317455Sdim#include <fcntl.h> 35317455Sdim#include <libgen.h> 36317455Sdim#include <libintl.h> 37353358Sdim#include <libuutil.h> 38353358Sdim#include <locale.h> 39353358Sdim#include <stdio.h> 40317455Sdim#include <stdlib.h> 41317455Sdim#include <string.h> 42317455Sdim#include <strings.h> 43317455Sdim#include <unistd.h> 44317455Sdim#include <priv.h> 45317455Sdim#include <sys/time.h> 46353358Sdim#include <sys/fs/zfs.h> 47353358Sdim 48353358Sdim#include <sys/stat.h> 49317455Sdim 50317455Sdim#include <libzfs.h> 51317455Sdim 52317455Sdim#include "zpool_util.h" 53317455Sdim 54317455Sdimstatic int zpool_do_create(int, char **); 55317455Sdimstatic int zpool_do_destroy(int, char **); 56317455Sdim 57353358Sdimstatic int zpool_do_add(int, char **); 58317455Sdimstatic int zpool_do_remove(int, char **); 59317455Sdim 60317455Sdimstatic int zpool_do_list(int, char **); 61353358Sdimstatic int zpool_do_iostat(int, char **); 62353358Sdimstatic int zpool_do_status(int, char **); 63353358Sdim 64317455Sdimstatic int zpool_do_online(int, char **); 65317455Sdimstatic int zpool_do_offline(int, char **); 66317455Sdimstatic int zpool_do_clear(int, char **); 67317455Sdim 68317455Sdimstatic int zpool_do_attach(int, char **); 69317455Sdimstatic int zpool_do_detach(int, char **); 70353358Sdimstatic int zpool_do_replace(int, char **); 71353358Sdim 72353358Sdimstatic int zpool_do_scrub(int, char **); 73317455Sdim 74317455Sdimstatic int zpool_do_import(int, char **); 75317455Sdimstatic int zpool_do_export(int, char **); 76317455Sdim 77317455Sdimstatic int zpool_do_upgrade(int, char **); 78317455Sdim 79317455Sdimstatic int zpool_do_history(int, char **); 80317455Sdim 81317455Sdimstatic int zpool_do_get(int, char **); 82317455Sdimstatic int zpool_do_set(int, char **); 83353358Sdim 84353358Sdim/* 85317455Sdim * These libumem hooks provide a reasonable set of defaults for the allocator's 86317455Sdim * debugging facilities. 87317455Sdim */ 88317455Sdimconst char * 89317455Sdim_umem_debug_init(void) 90353358Sdim{ 91353358Sdim return ("default,verbose"); /* $UMEM_DEBUG setting */ 92353358Sdim} 93317455Sdim 94317455Sdimconst char * 95317455Sdim_umem_logging_init(void) 96317455Sdim{ 97317455Sdim return ("fail,contents"); /* $UMEM_LOGGING setting */ 98353358Sdim} 99353358Sdim 100353358Sdimtypedef enum { 101317455Sdim HELP_ADD, 102317455Sdim HELP_ATTACH, 103317455Sdim HELP_CLEAR, 104317455Sdim HELP_CREATE, 105317455Sdim HELP_DESTROY, 106353358Sdim HELP_DETACH, 107353358Sdim HELP_EXPORT, 108353358Sdim HELP_HISTORY, 109353358Sdim HELP_IMPORT, 110353358Sdim HELP_IOSTAT, 111353358Sdim HELP_LIST, 112317455Sdim HELP_OFFLINE, 113317455Sdim HELP_ONLINE, 114317455Sdim HELP_REPLACE, 115317455Sdim HELP_REMOVE, 116317455Sdim HELP_SCRUB, 117317455Sdim HELP_STATUS, 118353358Sdim HELP_UPGRADE, 119353358Sdim HELP_GET, 120353358Sdim HELP_SET 121317455Sdim} zpool_help_t; 122317455Sdim 123317455Sdim 124317455Sdimtypedef struct zpool_command { 125317455Sdim const char *name; 126353358Sdim int (*func)(int, char **); 127353358Sdim zpool_help_t usage; 128317455Sdim} zpool_command_t; 129317455Sdim 130317455Sdim/* 131317455Sdim * Master command table. Each ZFS command has a name, associated function, and 132353358Sdim * usage message. The usage messages need to be internationalized, so we have 133353358Sdim * to have a function to return the usage message based on a command index. 134353358Sdim * 135353358Sdim * These commands are organized according to how they are displayed in the usage 136353358Sdim * message. An empty command (one with a NULL name) indicates an empty line in 137353358Sdim * the generic usage message. 138353358Sdim */ 139353358Sdimstatic zpool_command_t command_table[] = { 140353358Sdim { "create", zpool_do_create, HELP_CREATE }, 141353358Sdim { "destroy", zpool_do_destroy, HELP_DESTROY }, 142353358Sdim { NULL }, 143353358Sdim { "add", zpool_do_add, HELP_ADD }, 144353358Sdim { "remove", zpool_do_remove, HELP_REMOVE }, 145353358Sdim { NULL }, 146353358Sdim { "list", zpool_do_list, HELP_LIST }, 147353358Sdim { "iostat", zpool_do_iostat, HELP_IOSTAT }, 148353358Sdim { "status", zpool_do_status, HELP_STATUS }, 149353358Sdim { NULL }, 150353358Sdim { "online", zpool_do_online, HELP_ONLINE }, 151353358Sdim { "offline", zpool_do_offline, HELP_OFFLINE }, 152353358Sdim { "clear", zpool_do_clear, HELP_CLEAR }, 153353358Sdim { NULL }, 154353358Sdim { "attach", zpool_do_attach, HELP_ATTACH }, 155353358Sdim { "detach", zpool_do_detach, HELP_DETACH }, 156353358Sdim { "replace", zpool_do_replace, HELP_REPLACE }, 157353358Sdim { NULL }, 158353358Sdim { "scrub", zpool_do_scrub, HELP_SCRUB }, 159353358Sdim { NULL }, 160 { "import", zpool_do_import, HELP_IMPORT }, 161 { "export", zpool_do_export, HELP_EXPORT }, 162 { "upgrade", zpool_do_upgrade, HELP_UPGRADE }, 163 { NULL }, 164 { "history", zpool_do_history, HELP_HISTORY }, 165 { "get", zpool_do_get, HELP_GET }, 166 { "set", zpool_do_set, HELP_SET }, 167}; 168 169#define NCOMMAND (sizeof (command_table) / sizeof (command_table[0])) 170 171zpool_command_t *current_command; 172 173static const char * 174get_usage(zpool_help_t idx) { 175 switch (idx) { 176 case HELP_ADD: 177 return (gettext("\tadd [-fn] <pool> <vdev> ...\n")); 178 case HELP_ATTACH: 179 return (gettext("\tattach [-f] <pool> <device> " 180 "<new_device>\n")); 181 case HELP_CLEAR: 182 return (gettext("\tclear <pool> [device]\n")); 183 case HELP_CREATE: 184 return (gettext("\tcreate [-fn] [-R root] [-m mountpoint] " 185 "<pool> <vdev> ...\n")); 186 case HELP_DESTROY: 187 return (gettext("\tdestroy [-f] <pool>\n")); 188 case HELP_DETACH: 189 return (gettext("\tdetach <pool> <device>\n")); 190 case HELP_EXPORT: 191 return (gettext("\texport [-f] <pool> ...\n")); 192 case HELP_HISTORY: 193 return (gettext("\thistory [<pool>]\n")); 194 case HELP_IMPORT: 195 return (gettext("\timport [-d dir] [-D]\n" 196 "\timport [-d dir] [-D] [-f] [-o opts] [-R root] -a\n" 197 "\timport [-d dir] [-D] [-f] [-o opts] [-R root ]" 198 " <pool | id> [newpool]\n")); 199 case HELP_IOSTAT: 200 return (gettext("\tiostat [-v] [pool] ... [interval " 201 "[count]]\n")); 202 case HELP_LIST: 203 return (gettext("\tlist [-H] [-o field[,field]*] " 204 "[pool] ...\n")); 205 case HELP_OFFLINE: 206 return (gettext("\toffline [-t] <pool> <device> ...\n")); 207 case HELP_ONLINE: 208 return (gettext("\tonline <pool> <device> ...\n")); 209 case HELP_REPLACE: 210 return (gettext("\treplace [-f] <pool> <device> " 211 "[new_device]\n")); 212 case HELP_REMOVE: 213 return (gettext("\tremove <pool> <device>\n")); 214 case HELP_SCRUB: 215 return (gettext("\tscrub [-s] <pool> ...\n")); 216 case HELP_STATUS: 217 return (gettext("\tstatus [-vx] [pool] ...\n")); 218 case HELP_UPGRADE: 219 return (gettext("\tupgrade\n" 220 "\tupgrade -v\n" 221 "\tupgrade <-a | pool>\n")); 222 case HELP_GET: 223 return (gettext("\tget <all | property[,property]...> " 224 "<pool> ...\n")); 225 case HELP_SET: 226 return (gettext("\tset <property=value> <pool> \n")); 227 } 228 229 abort(); 230 /* NOTREACHED */ 231} 232 233/* 234 * Fields available for 'zpool list'. 235 */ 236typedef enum { 237 ZPOOL_FIELD_NAME, 238 ZPOOL_FIELD_SIZE, 239 ZPOOL_FIELD_USED, 240 ZPOOL_FIELD_AVAILABLE, 241 ZPOOL_FIELD_CAPACITY, 242 ZPOOL_FIELD_HEALTH, 243 ZPOOL_FIELD_ROOT 244} zpool_field_t; 245 246#define MAX_FIELDS 10 247 248typedef struct column_def { 249 const char *cd_title; 250 size_t cd_width; 251 enum { 252 left_justify, 253 right_justify 254 } cd_justify; 255} column_def_t; 256 257static column_def_t column_table[] = { 258 { "NAME", 20, left_justify }, 259 { "SIZE", 6, right_justify }, 260 { "USED", 6, right_justify }, 261 { "AVAIL", 6, right_justify }, 262 { "CAP", 5, right_justify }, 263 { "HEALTH", 9, left_justify }, 264 { "ALTROOT", 15, left_justify } 265}; 266 267static char *column_subopts[] = { 268 "name", 269 "size", 270 "used", 271 "available", 272 "capacity", 273 "health", 274 "root", 275 NULL 276}; 277 278/* 279 * Callback routine that will print out a pool property value. 280 */ 281static zpool_prop_t 282print_prop_cb(zpool_prop_t prop, void *cb) 283{ 284 FILE *fp = cb; 285 286 (void) fprintf(fp, "\t%-13s ", zpool_prop_to_name(prop)); 287 288 if (zpool_prop_values(prop) == NULL) 289 (void) fprintf(fp, "-\n"); 290 else 291 (void) fprintf(fp, "%s\n", zpool_prop_values(prop)); 292 293 return (ZFS_PROP_CONT); 294} 295 296/* 297 * Display usage message. If we're inside a command, display only the usage for 298 * that command. Otherwise, iterate over the entire command table and display 299 * a complete usage message. 300 */ 301void 302usage(boolean_t requested) 303{ 304 int i; 305 FILE *fp = requested ? stdout : stderr; 306 307 if (current_command == NULL) { 308 int i; 309 310 (void) fprintf(fp, gettext("usage: zpool command args ...\n")); 311 (void) fprintf(fp, 312 gettext("where 'command' is one of the following:\n\n")); 313 314 for (i = 0; i < NCOMMAND; i++) { 315 if (command_table[i].name == NULL) 316 (void) fprintf(fp, "\n"); 317 else 318 (void) fprintf(fp, "%s", 319 get_usage(command_table[i].usage)); 320 } 321 } else { 322 (void) fprintf(fp, gettext("usage:\n")); 323 (void) fprintf(fp, "%s", get_usage(current_command->usage)); 324 325 if (strcmp(current_command->name, "list") == 0) { 326 (void) fprintf(fp, gettext("\nwhere 'field' is one " 327 "of the following:\n\n")); 328 329 for (i = 0; column_subopts[i] != NULL; i++) 330 (void) fprintf(fp, "\t%s\n", column_subopts[i]); 331 } 332 } 333 334 if (current_command != NULL && 335 ((strcmp(current_command->name, "set") == 0) || 336 (strcmp(current_command->name, "get") == 0))) { 337 338 (void) fprintf(fp, 339 gettext("\nthe following properties are supported:\n")); 340 341 (void) fprintf(fp, "\n\t%-13s %s\n\n", 342 "PROPERTY", "VALUES"); 343 344 /* Iterate over all properties */ 345 (void) zpool_prop_iter(print_prop_cb, fp, B_FALSE); 346 } 347 348 /* 349 * See comments at end of main(). 350 */ 351 if (getenv("ZFS_ABORT") != NULL) { 352 (void) printf("dumping core by request\n"); 353 abort(); 354 } 355 356 exit(requested ? 0 : 2); 357} 358 359const char * 360state_to_health(int vs_state) 361{ 362 switch (vs_state) { 363 case VDEV_STATE_CLOSED: 364 case VDEV_STATE_CANT_OPEN: 365 case VDEV_STATE_OFFLINE: 366 return (dgettext(TEXT_DOMAIN, "FAULTED")); 367 case VDEV_STATE_DEGRADED: 368 return (dgettext(TEXT_DOMAIN, "DEGRADED")); 369 case VDEV_STATE_HEALTHY: 370 return (dgettext(TEXT_DOMAIN, "ONLINE")); 371 } 372 373 return (dgettext(TEXT_DOMAIN, "UNKNOWN")); 374} 375 376const char * 377state_to_name(vdev_stat_t *vs) 378{ 379 switch (vs->vs_state) { 380 case VDEV_STATE_CLOSED: 381 case VDEV_STATE_CANT_OPEN: 382 if (vs->vs_aux == VDEV_AUX_CORRUPT_DATA) 383 return (gettext("FAULTED")); 384 else 385 return (gettext("UNAVAIL")); 386 case VDEV_STATE_OFFLINE: 387 return (gettext("OFFLINE")); 388 case VDEV_STATE_DEGRADED: 389 return (gettext("DEGRADED")); 390 case VDEV_STATE_HEALTHY: 391 return (gettext("ONLINE")); 392 } 393 394 return (gettext("UNKNOWN")); 395} 396 397void 398print_vdev_tree(zpool_handle_t *zhp, const char *name, nvlist_t *nv, int indent) 399{ 400 nvlist_t **child; 401 uint_t c, children; 402 char *vname; 403 404 if (name != NULL) 405 (void) printf("\t%*s%s\n", indent, "", name); 406 407 if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN, 408 &child, &children) != 0) 409 return; 410 411 for (c = 0; c < children; c++) { 412 vname = zpool_vdev_name(g_zfs, zhp, child[c]); 413 print_vdev_tree(zhp, vname, child[c], indent + 2); 414 free(vname); 415 } 416} 417 418/* 419 * zpool add [-fn] <pool> <vdev> ... 420 * 421 * -f Force addition of devices, even if they appear in use 422 * -n Do not add the devices, but display the resulting layout if 423 * they were to be added. 424 * 425 * Adds the given vdevs to 'pool'. As with create, the bulk of this work is 426 * handled by get_vdev_spec(), which constructs the nvlist needed to pass to 427 * libzfs. 428 */ 429int 430zpool_do_add(int argc, char **argv) 431{ 432 boolean_t force = B_FALSE; 433 boolean_t dryrun = B_FALSE; 434 int c; 435 nvlist_t *nvroot; 436 char *poolname; 437 int ret; 438 zpool_handle_t *zhp; 439 nvlist_t *config; 440 441 /* check options */ 442 while ((c = getopt(argc, argv, "fn")) != -1) { 443 switch (c) { 444 case 'f': 445 force = B_TRUE; 446 break; 447 case 'n': 448 dryrun = B_TRUE; 449 break; 450 case '?': 451 (void) fprintf(stderr, gettext("invalid option '%c'\n"), 452 optopt); 453 usage(B_FALSE); 454 } 455 } 456 457 argc -= optind; 458 argv += optind; 459 460 /* get pool name and check number of arguments */ 461 if (argc < 1) { 462 (void) fprintf(stderr, gettext("missing pool name argument\n")); 463 usage(B_FALSE); 464 } 465 if (argc < 2) { 466 (void) fprintf(stderr, gettext("missing vdev specification\n")); 467 usage(B_FALSE); 468 } 469 470 poolname = argv[0]; 471 472 argc--; 473 argv++; 474 475 if ((zhp = zpool_open(g_zfs, poolname)) == NULL) 476 return (1); 477 478 if ((config = zpool_get_config(zhp, NULL)) == NULL) { 479 (void) fprintf(stderr, gettext("pool '%s' is unavailable\n"), 480 poolname); 481 zpool_close(zhp); 482 return (1); 483 } 484 485 /* pass off to get_vdev_spec for processing */ 486 nvroot = make_root_vdev(config, force, !force, B_FALSE, argc, argv); 487 if (nvroot == NULL) { 488 zpool_close(zhp); 489 return (1); 490 } 491 492 if (dryrun) { 493 nvlist_t *poolnvroot; 494 495 verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, 496 &poolnvroot) == 0); 497 498 (void) printf(gettext("would update '%s' to the following " 499 "configuration:\n"), zpool_get_name(zhp)); 500 501 print_vdev_tree(zhp, poolname, poolnvroot, 0); 502 print_vdev_tree(zhp, NULL, nvroot, 0); 503 504 ret = 0; 505 } else { 506 ret = (zpool_add(zhp, nvroot) != 0); 507 if (!ret) { 508 zpool_log_history(g_zfs, argc + 1 + optind, 509 argv - 1 - optind, poolname, B_TRUE, B_FALSE); 510 } 511 } 512 513 nvlist_free(nvroot); 514 zpool_close(zhp); 515 516 return (ret); 517} 518 519/* 520 * zpool remove <pool> <vdev> 521 * 522 * Removes the given vdev from the pool. Currently, this only supports removing 523 * spares from the pool. Eventually, we'll want to support removing leaf vdevs 524 * (as an alias for 'detach') as well as toplevel vdevs. 525 */ 526int 527zpool_do_remove(int argc, char **argv) 528{ 529 char *poolname; 530 int ret; 531 zpool_handle_t *zhp; 532 533 argc--; 534 argv++; 535 536 /* get pool name and check number of arguments */ 537 if (argc < 1) { 538 (void) fprintf(stderr, gettext("missing pool name argument\n")); 539 usage(B_FALSE); 540 } 541 if (argc < 2) { 542 (void) fprintf(stderr, gettext("missing device\n")); 543 usage(B_FALSE); 544 } 545 546 poolname = argv[0]; 547 548 if ((zhp = zpool_open(g_zfs, poolname)) == NULL) 549 return (1); 550 551 ret = (zpool_vdev_remove(zhp, argv[1]) != 0); 552 if (!ret) { 553 zpool_log_history(g_zfs, ++argc, --argv, poolname, B_TRUE, 554 B_FALSE); 555 } 556 557 return (ret); 558} 559 560/* 561 * zpool create [-fn] [-R root] [-m mountpoint] <pool> <dev> ... 562 * 563 * -f Force creation, even if devices appear in use 564 * -n Do not create the pool, but display the resulting layout if it 565 * were to be created. 566 * -R Create a pool under an alternate root 567 * -m Set default mountpoint for the root dataset. By default it's 568 * '/<pool>' 569 * 570 * Creates the named pool according to the given vdev specification. The 571 * bulk of the vdev processing is done in get_vdev_spec() in zpool_vdev.c. Once 572 * we get the nvlist back from get_vdev_spec(), we either print out the contents 573 * (if '-n' was specified), or pass it to libzfs to do the creation. 574 */ 575int 576zpool_do_create(int argc, char **argv) 577{ 578 boolean_t force = B_FALSE; 579 boolean_t dryrun = B_FALSE; 580 int c; 581 nvlist_t *nvroot; 582 char *poolname; 583 int ret; 584 char *altroot = NULL; 585 char *mountpoint = NULL; 586 nvlist_t **child; 587 uint_t children; 588 589 /* check options */ 590 while ((c = getopt(argc, argv, ":fnR:m:")) != -1) { 591 switch (c) { 592 case 'f': 593 force = B_TRUE; 594 break; 595 case 'n': 596 dryrun = B_TRUE; 597 break; 598 case 'R': 599 altroot = optarg; 600 break; 601 case 'm': 602 mountpoint = optarg; 603 break; 604 case ':': 605 (void) fprintf(stderr, gettext("missing argument for " 606 "'%c' option\n"), optopt); 607 usage(B_FALSE); 608 break; 609 case '?': 610 (void) fprintf(stderr, gettext("invalid option '%c'\n"), 611 optopt); 612 usage(B_FALSE); 613 } 614 } 615 616 argc -= optind; 617 argv += optind; 618 619 /* get pool name and check number of arguments */ 620 if (argc < 1) { 621 (void) fprintf(stderr, gettext("missing pool name argument\n")); 622 usage(B_FALSE); 623 } 624 if (argc < 2) { 625 (void) fprintf(stderr, gettext("missing vdev specification\n")); 626 usage(B_FALSE); 627 } 628 629 poolname = argv[0]; 630 631 /* 632 * As a special case, check for use of '/' in the name, and direct the 633 * user to use 'zfs create' instead. 634 */ 635 if (strchr(poolname, '/') != NULL) { 636 (void) fprintf(stderr, gettext("cannot create '%s': invalid " 637 "character '/' in pool name\n"), poolname); 638 (void) fprintf(stderr, gettext("use 'zfs create' to " 639 "create a dataset\n")); 640 return (1); 641 } 642 643 /* pass off to get_vdev_spec for bulk processing */ 644 nvroot = make_root_vdev(NULL, force, !force, B_FALSE, argc - 1, 645 argv + 1); 646 if (nvroot == NULL) 647 return (1); 648 649 /* make_root_vdev() allows 0 toplevel children if there are spares */ 650 verify(nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN, 651 &child, &children) == 0); 652 if (children == 0) { 653 (void) fprintf(stderr, gettext("invalid vdev " 654 "specification: at least one toplevel vdev must be " 655 "specified\n")); 656 return (1); 657 } 658 659 660 if (altroot != NULL && altroot[0] != '/') { 661 (void) fprintf(stderr, gettext("invalid alternate root '%s': " 662 "must be an absolute path\n"), altroot); 663 nvlist_free(nvroot); 664 return (1); 665 } 666 667 /* 668 * Check the validity of the mountpoint and direct the user to use the 669 * '-m' mountpoint option if it looks like its in use. 670 */ 671 if (mountpoint == NULL || 672 (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) != 0 && 673 strcmp(mountpoint, ZFS_MOUNTPOINT_NONE) != 0)) { 674 char buf[MAXPATHLEN]; 675 struct stat64 statbuf; 676 677 if (mountpoint && mountpoint[0] != '/') { 678 (void) fprintf(stderr, gettext("invalid mountpoint " 679 "'%s': must be an absolute path, 'legacy', or " 680 "'none'\n"), mountpoint); 681 nvlist_free(nvroot); 682 return (1); 683 } 684 685 if (mountpoint == NULL) { 686 if (altroot != NULL) 687 (void) snprintf(buf, sizeof (buf), "%s/%s", 688 altroot, poolname); 689 else 690 (void) snprintf(buf, sizeof (buf), "/%s", 691 poolname); 692 } else { 693 if (altroot != NULL) 694 (void) snprintf(buf, sizeof (buf), "%s%s", 695 altroot, mountpoint); 696 else 697 (void) snprintf(buf, sizeof (buf), "%s", 698 mountpoint); 699 } 700 701 if (stat64(buf, &statbuf) == 0 && 702 statbuf.st_nlink != 2) { 703 if (mountpoint == NULL) 704 (void) fprintf(stderr, gettext("default " 705 "mountpoint '%s' exists and is not " 706 "empty\n"), buf); 707 else 708 (void) fprintf(stderr, gettext("mountpoint " 709 "'%s' exists and is not empty\n"), buf); 710 (void) fprintf(stderr, gettext("use '-m' " 711 "option to provide a different default\n")); 712 nvlist_free(nvroot); 713 return (1); 714 } 715 } 716 717 718 if (dryrun) { 719 /* 720 * For a dry run invocation, print out a basic message and run 721 * through all the vdevs in the list and print out in an 722 * appropriate hierarchy. 723 */ 724 (void) printf(gettext("would create '%s' with the " 725 "following layout:\n\n"), poolname); 726 727 print_vdev_tree(NULL, poolname, nvroot, 0); 728 729 ret = 0; 730 } else { 731 ret = 1; 732 /* 733 * Hand off to libzfs. 734 */ 735 if (zpool_create(g_zfs, poolname, nvroot, altroot) == 0) { 736 zfs_handle_t *pool = zfs_open(g_zfs, poolname, 737 ZFS_TYPE_FILESYSTEM); 738 if (pool != NULL) { 739 if (mountpoint != NULL) 740 verify(zfs_prop_set(pool, 741 zfs_prop_to_name( 742 ZFS_PROP_MOUNTPOINT), 743 mountpoint) == 0); 744 if (zfs_mount(pool, NULL, 0) == 0) 745 ret = zfs_share_nfs(pool); 746 zfs_close(pool); 747 } 748 zpool_log_history(g_zfs, argc + optind, argv - optind, 749 poolname, B_TRUE, B_TRUE); 750 } else if (libzfs_errno(g_zfs) == EZFS_INVALIDNAME) { 751 (void) fprintf(stderr, gettext("pool name may have " 752 "been omitted\n")); 753 } 754 } 755 756 nvlist_free(nvroot); 757 758 return (ret); 759} 760 761/* 762 * zpool destroy <pool> 763 * 764 * -f Forcefully unmount any datasets 765 * 766 * Destroy the given pool. Automatically unmounts any datasets in the pool. 767 */ 768int 769zpool_do_destroy(int argc, char **argv) 770{ 771 boolean_t force = B_FALSE; 772 int c; 773 char *pool; 774 zpool_handle_t *zhp; 775 int ret; 776 777 /* check options */ 778 while ((c = getopt(argc, argv, "f")) != -1) { 779 switch (c) { 780 case 'f': 781 force = B_TRUE; 782 break; 783 case '?': 784 (void) fprintf(stderr, gettext("invalid option '%c'\n"), 785 optopt); 786 usage(B_FALSE); 787 } 788 } 789 790 argc -= optind; 791 argv += optind; 792 793 /* check arguments */ 794 if (argc < 1) { 795 (void) fprintf(stderr, gettext("missing pool argument\n")); 796 usage(B_FALSE); 797 } 798 if (argc > 1) { 799 (void) fprintf(stderr, gettext("too many arguments\n")); 800 usage(B_FALSE); 801 } 802 803 pool = argv[0]; 804 805 if ((zhp = zpool_open_canfail(g_zfs, pool)) == NULL) { 806 /* 807 * As a special case, check for use of '/' in the name, and 808 * direct the user to use 'zfs destroy' instead. 809 */ 810 if (strchr(pool, '/') != NULL) 811 (void) fprintf(stderr, gettext("use 'zfs destroy' to " 812 "destroy a dataset\n")); 813 return (1); 814 } 815 816 if (zpool_disable_datasets(zhp, force) != 0) { 817 (void) fprintf(stderr, gettext("could not destroy '%s': " 818 "could not unmount datasets\n"), zpool_get_name(zhp)); 819 return (1); 820 } 821 822 zpool_log_history(g_zfs, argc + optind, argv - optind, pool, B_TRUE, 823 B_FALSE); 824 825 ret = (zpool_destroy(zhp) != 0); 826 827 zpool_close(zhp); 828 829 return (ret); 830} 831 832/* 833 * zpool export [-f] <pool> ... 834 * 835 * -f Forcefully unmount datasets 836 * 837 * Export the given pools. By default, the command will attempt to cleanly 838 * unmount any active datasets within the pool. If the '-f' flag is specified, 839 * then the datasets will be forcefully unmounted. 840 */ 841int 842zpool_do_export(int argc, char **argv) 843{ 844 boolean_t force = B_FALSE; 845 int c; 846 zpool_handle_t *zhp; 847 int ret; 848 int i; 849 850 /* check options */ 851 while ((c = getopt(argc, argv, "f")) != -1) { 852 switch (c) { 853 case 'f': 854 force = B_TRUE; 855 break; 856 case '?': 857 (void) fprintf(stderr, gettext("invalid option '%c'\n"), 858 optopt); 859 usage(B_FALSE); 860 } 861 } 862 863 argc -= optind; 864 argv += optind; 865 866 /* check arguments */ 867 if (argc < 1) { 868 (void) fprintf(stderr, gettext("missing pool argument\n")); 869 usage(B_FALSE); 870 } 871 872 ret = 0; 873 for (i = 0; i < argc; i++) { 874 if ((zhp = zpool_open_canfail(g_zfs, argv[i])) == NULL) { 875 ret = 1; 876 continue; 877 } 878 879 if (zpool_disable_datasets(zhp, force) != 0) { 880 ret = 1; 881 zpool_close(zhp); 882 continue; 883 } 884 885 zpool_log_history(g_zfs, argc + optind, argv - optind, argv[i], 886 B_TRUE, B_FALSE); 887 888 if (zpool_export(zhp) != 0) 889 ret = 1; 890 891 zpool_close(zhp); 892 } 893 894 return (ret); 895} 896 897/* 898 * Given a vdev configuration, determine the maximum width needed for the device 899 * name column. 900 */ 901static int 902max_width(zpool_handle_t *zhp, nvlist_t *nv, int depth, int max) 903{ 904 char *name = zpool_vdev_name(g_zfs, zhp, nv); 905 nvlist_t **child; 906 uint_t c, children; 907 int ret; 908 909 if (strlen(name) + depth > max) 910 max = strlen(name) + depth; 911 912 free(name); 913 914 if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_SPARES, 915 &child, &children) == 0) { 916 for (c = 0; c < children; c++) 917 if ((ret = max_width(zhp, child[c], depth + 2, 918 max)) > max) 919 max = ret; 920 } 921 922 if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN, 923 &child, &children) == 0) { 924 for (c = 0; c < children; c++) 925 if ((ret = max_width(zhp, child[c], depth + 2, 926 max)) > max) 927 max = ret; 928 } 929 930 931 return (max); 932} 933 934 935/* 936 * Print the configuration of an exported pool. Iterate over all vdevs in the 937 * pool, printing out the name and status for each one. 938 */ 939void 940print_import_config(const char *name, nvlist_t *nv, int namewidth, int depth) 941{ 942 nvlist_t **child; 943 uint_t c, children; 944 vdev_stat_t *vs; 945 char *type, *vname; 946 947 verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE, &type) == 0); 948 if (strcmp(type, VDEV_TYPE_MISSING) == 0) 949 return; 950 951 verify(nvlist_lookup_uint64_array(nv, ZPOOL_CONFIG_STATS, 952 (uint64_t **)&vs, &c) == 0); 953 954 (void) printf("\t%*s%-*s", depth, "", namewidth - depth, name); 955 956 if (vs->vs_aux != 0) { 957 (void) printf(" %-8s ", state_to_name(vs)); 958 959 switch (vs->vs_aux) { 960 case VDEV_AUX_OPEN_FAILED: 961 (void) printf(gettext("cannot open")); 962 break; 963 964 case VDEV_AUX_BAD_GUID_SUM: 965 (void) printf(gettext("missing device")); 966 break; 967 968 case VDEV_AUX_NO_REPLICAS: 969 (void) printf(gettext("insufficient replicas")); 970 break; 971 972 case VDEV_AUX_VERSION_NEWER: 973 (void) printf(gettext("newer version")); 974 break; 975 976 default: 977 (void) printf(gettext("corrupted data")); 978 break; 979 } 980 } else { 981 (void) printf(" %s", state_to_name(vs)); 982 } 983 (void) printf("\n"); 984 985 if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN, 986 &child, &children) != 0) 987 return; 988 989 for (c = 0; c < children; c++) { 990 vname = zpool_vdev_name(g_zfs, NULL, child[c]); 991 print_import_config(vname, child[c], 992 namewidth, depth + 2); 993 free(vname); 994 } 995 996 if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_SPARES, 997 &child, &children) != 0) 998 return; 999 1000 (void) printf(gettext("\tspares\n")); 1001 for (c = 0; c < children; c++) { 1002 vname = zpool_vdev_name(g_zfs, NULL, child[c]); 1003 (void) printf("\t %s\n", vname); 1004 free(vname); 1005 } 1006} 1007 1008/* 1009 * Display the status for the given pool. 1010 */ 1011static void 1012show_import(nvlist_t *config) 1013{ 1014 uint64_t pool_state; 1015 vdev_stat_t *vs; 1016 char *name; 1017 uint64_t guid; 1018 char *msgid; 1019 nvlist_t *nvroot; 1020 int reason; 1021 const char *health; 1022 uint_t vsc; 1023 int namewidth; 1024 1025 verify(nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME, 1026 &name) == 0); 1027 verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, 1028 &guid) == 0); 1029 verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_STATE, 1030 &pool_state) == 0); 1031 verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, 1032 &nvroot) == 0); 1033 1034 verify(nvlist_lookup_uint64_array(nvroot, ZPOOL_CONFIG_STATS, 1035 (uint64_t **)&vs, &vsc) == 0); 1036 health = state_to_health(vs->vs_state); 1037 1038 reason = zpool_import_status(config, &msgid); 1039 1040 (void) printf(gettext(" pool: %s\n"), name); 1041 (void) printf(gettext(" id: %llu\n"), (u_longlong_t)guid); 1042 (void) printf(gettext(" state: %s"), health); 1043 if (pool_state == POOL_STATE_DESTROYED) 1044 (void) printf(gettext(" (DESTROYED)")); 1045 (void) printf("\n"); 1046 1047 switch (reason) { 1048 case ZPOOL_STATUS_MISSING_DEV_R: 1049 case ZPOOL_STATUS_MISSING_DEV_NR: 1050 case ZPOOL_STATUS_BAD_GUID_SUM: 1051 (void) printf(gettext("status: One or more devices are missing " 1052 "from the system.\n")); 1053 break; 1054 1055 case ZPOOL_STATUS_CORRUPT_LABEL_R: 1056 case ZPOOL_STATUS_CORRUPT_LABEL_NR: 1057 (void) printf(gettext("status: One or more devices contains " 1058 "corrupted data.\n")); 1059 break; 1060 1061 case ZPOOL_STATUS_CORRUPT_DATA: 1062 (void) printf(gettext("status: The pool data is corrupted.\n")); 1063 break; 1064 1065 case ZPOOL_STATUS_OFFLINE_DEV: 1066 (void) printf(gettext("status: One or more devices " 1067 "are offlined.\n")); 1068 break; 1069 1070 case ZPOOL_STATUS_CORRUPT_POOL: 1071 (void) printf(gettext("status: The pool metadata is " 1072 "corrupted.\n")); 1073 break; 1074 1075 case ZPOOL_STATUS_VERSION_OLDER: 1076 (void) printf(gettext("status: The pool is formatted using an " 1077 "older on-disk version.\n")); 1078 break; 1079 1080 case ZPOOL_STATUS_VERSION_NEWER: 1081 (void) printf(gettext("status: The pool is formatted using an " 1082 "incompatible version.\n")); 1083 break; 1084 1085 case ZPOOL_STATUS_HOSTID_MISMATCH: 1086 (void) printf(gettext("status: The pool was last accessed by " 1087 "another system.\n")); 1088 break; 1089 default: 1090 /* 1091 * No other status can be seen when importing pools. 1092 */ 1093 assert(reason == ZPOOL_STATUS_OK); 1094 } 1095 1096 /* 1097 * Print out an action according to the overall state of the pool. 1098 */ 1099 if (vs->vs_state == VDEV_STATE_HEALTHY) { 1100 if (reason == ZPOOL_STATUS_VERSION_OLDER) 1101 (void) printf(gettext("action: The pool can be " 1102 "imported using its name or numeric identifier, " 1103 "though\n\tsome features will not be available " 1104 "without an explicit 'zpool upgrade'.\n")); 1105 else if (reason == ZPOOL_STATUS_HOSTID_MISMATCH) 1106 (void) printf(gettext("action: The pool can be " 1107 "imported using its name or numeric " 1108 "identifier and\n\tthe '-f' flag.\n")); 1109 else 1110 (void) printf(gettext("action: The pool can be " 1111 "imported using its name or numeric " 1112 "identifier.\n")); 1113 } else if (vs->vs_state == VDEV_STATE_DEGRADED) { 1114 (void) printf(gettext("action: The pool can be imported " 1115 "despite missing or damaged devices. The\n\tfault " 1116 "tolerance of the pool may be compromised if imported.\n")); 1117 } else { 1118 switch (reason) { 1119 case ZPOOL_STATUS_VERSION_NEWER: 1120 (void) printf(gettext("action: The pool cannot be " 1121 "imported. Access the pool on a system running " 1122 "newer\n\tsoftware, or recreate the pool from " 1123 "backup.\n")); 1124 break; 1125 case ZPOOL_STATUS_MISSING_DEV_R: 1126 case ZPOOL_STATUS_MISSING_DEV_NR: 1127 case ZPOOL_STATUS_BAD_GUID_SUM: 1128 (void) printf(gettext("action: The pool cannot be " 1129 "imported. Attach the missing\n\tdevices and try " 1130 "again.\n")); 1131 break; 1132 default: 1133 (void) printf(gettext("action: The pool cannot be " 1134 "imported due to damaged devices or data.\n")); 1135 } 1136 } 1137 1138 /* 1139 * If the state is "closed" or "can't open", and the aux state 1140 * is "corrupt data": 1141 */ 1142 if (((vs->vs_state == VDEV_STATE_CLOSED) || 1143 (vs->vs_state == VDEV_STATE_CANT_OPEN)) && 1144 (vs->vs_aux == VDEV_AUX_CORRUPT_DATA)) { 1145 if (pool_state == POOL_STATE_DESTROYED) 1146 (void) printf(gettext("\tThe pool was destroyed, " 1147 "but can be imported using the '-Df' flags.\n")); 1148 else if (pool_state != POOL_STATE_EXPORTED) 1149 (void) printf(gettext("\tThe pool may be active on " 1150 "on another system, but can be imported using\n\t" 1151 "the '-f' flag.\n")); 1152 } 1153 1154 if (msgid != NULL) 1155 (void) printf(gettext(" see: http://www.sun.com/msg/%s\n"), 1156 msgid); 1157 1158 (void) printf(gettext("config:\n\n")); 1159 1160 namewidth = max_width(NULL, nvroot, 0, 0); 1161 if (namewidth < 10) 1162 namewidth = 10; 1163 print_import_config(name, nvroot, namewidth, 0); 1164 1165 if (reason == ZPOOL_STATUS_BAD_GUID_SUM) { 1166 (void) printf(gettext("\n\tAdditional devices are known to " 1167 "be part of this pool, though their\n\texact " 1168 "configuration cannot be determined.\n")); 1169 } 1170} 1171 1172/* 1173 * Perform the import for the given configuration. This passes the heavy 1174 * lifting off to zpool_import(), and then mounts the datasets contained within 1175 * the pool. 1176 */ 1177static int 1178do_import(nvlist_t *config, const char *newname, const char *mntopts, 1179 const char *altroot, int force, int argc, char **argv) 1180{ 1181 zpool_handle_t *zhp; 1182 char *name; 1183 uint64_t state; 1184 uint64_t version; 1185 1186 verify(nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME, 1187 &name) == 0); 1188 1189 verify(nvlist_lookup_uint64(config, 1190 ZPOOL_CONFIG_POOL_STATE, &state) == 0); 1191 verify(nvlist_lookup_uint64(config, 1192 ZPOOL_CONFIG_VERSION, &version) == 0); 1193 if (version > ZFS_VERSION) { 1194 (void) fprintf(stderr, gettext("cannot import '%s': pool " 1195 "is formatted using a newer ZFS version\n"), name); 1196 return (1); 1197 } else if (state != POOL_STATE_EXPORTED && !force) { 1198 uint64_t hostid; 1199 1200 if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_HOSTID, 1201 &hostid) == 0) { 1202 if ((unsigned long)hostid != gethostid()) { 1203 char *hostname; 1204 uint64_t timestamp; 1205 time_t t; 1206 1207 verify(nvlist_lookup_string(config, 1208 ZPOOL_CONFIG_HOSTNAME, &hostname) == 0); 1209 verify(nvlist_lookup_uint64(config, 1210 ZPOOL_CONFIG_TIMESTAMP, ×tamp) == 0); 1211 t = timestamp; 1212 (void) fprintf(stderr, gettext("cannot import " 1213 "'%s': pool may be in use from other " 1214 "system, it was last accessed by %s " 1215 "(hostid: 0x%lx) on %s"), name, hostname, 1216 (unsigned long)hostid, 1217 asctime(localtime(&t))); 1218 (void) fprintf(stderr, gettext("use '-f' to " 1219 "import anyway\n")); 1220 return (1); 1221 } 1222 } else { 1223 (void) fprintf(stderr, gettext("cannot import '%s': " 1224 "pool may be in use from other system\n"), name); 1225 (void) fprintf(stderr, gettext("use '-f' to import " 1226 "anyway\n")); 1227 return (1); 1228 } 1229 } 1230 1231 if (zpool_import(g_zfs, config, newname, altroot) != 0) 1232 return (1); 1233 1234 if (newname != NULL) 1235 name = (char *)newname; 1236 1237 zpool_log_history(g_zfs, argc, argv, name, B_TRUE, B_FALSE); 1238 1239 verify((zhp = zpool_open(g_zfs, name)) != NULL); 1240 1241 if (zpool_enable_datasets(zhp, mntopts, 0) != 0) { 1242 zpool_close(zhp); 1243 return (1); 1244 } 1245 1246 zpool_close(zhp); 1247 return (0); 1248} 1249 1250/* 1251 * zpool import [-d dir] [-D] 1252 * import [-R root] [-D] [-d dir] [-f] -a 1253 * import [-R root] [-D] [-d dir] [-f] <pool | id> [newpool] 1254 * 1255 * -d Scan in a specific directory, other than /dev/dsk. More than 1256 * one directory can be specified using multiple '-d' options. 1257 * 1258 * -D Scan for previously destroyed pools or import all or only 1259 * specified destroyed pools. 1260 * 1261 * -R Temporarily import the pool, with all mountpoints relative to 1262 * the given root. The pool will remain exported when the machine 1263 * is rebooted. 1264 * 1265 * -f Force import, even if it appears that the pool is active. 1266 * 1267 * -a Import all pools found. 1268 * 1269 * The import command scans for pools to import, and import pools based on pool 1270 * name and GUID. The pool can also be renamed as part of the import process. 1271 */ 1272int 1273zpool_do_import(int argc, char **argv) 1274{ 1275 char **searchdirs = NULL; 1276 int nsearch = 0; 1277 int c; 1278 int err; 1279 nvlist_t *pools; 1280 boolean_t do_all = B_FALSE; 1281 boolean_t do_destroyed = B_FALSE; 1282 char *altroot = NULL; 1283 char *mntopts = NULL; 1284 boolean_t do_force = B_FALSE; 1285 nvpair_t *elem; 1286 nvlist_t *config; 1287 uint64_t searchguid; 1288 char *searchname; 1289 nvlist_t *found_config; 1290 boolean_t first; 1291 uint64_t pool_state; 1292 1293 /* check options */ 1294 while ((c = getopt(argc, argv, ":Dfd:R:ao:")) != -1) { 1295 switch (c) { 1296 case 'a': 1297 do_all = B_TRUE; 1298 break; 1299 case 'd': 1300 if (searchdirs == NULL) { 1301 searchdirs = safe_malloc(sizeof (char *)); 1302 } else { 1303 char **tmp = safe_malloc((nsearch + 1) * 1304 sizeof (char *)); 1305 bcopy(searchdirs, tmp, nsearch * 1306 sizeof (char *)); 1307 free(searchdirs); 1308 searchdirs = tmp; 1309 } 1310 searchdirs[nsearch++] = optarg; 1311 break; 1312 case 'D': 1313 do_destroyed = B_TRUE; 1314 break; 1315 case 'f': 1316 do_force = B_TRUE; 1317 break; 1318 case 'o': 1319 mntopts = optarg; 1320 break; 1321 case 'R': 1322 altroot = optarg; 1323 break; 1324 case ':': 1325 (void) fprintf(stderr, gettext("missing argument for " 1326 "'%c' option\n"), optopt); 1327 usage(B_FALSE); 1328 break; 1329 case '?': 1330 (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1331 optopt); 1332 usage(B_FALSE); 1333 } 1334 } 1335 1336 argc -= optind; 1337 argv += optind; 1338 1339 if (searchdirs == NULL) { 1340 searchdirs = safe_malloc(sizeof (char *)); 1341 searchdirs[0] = "/dev"; 1342 nsearch = 1; 1343 } 1344 1345 /* check argument count */ 1346 if (do_all) { 1347 if (argc != 0) { 1348 (void) fprintf(stderr, gettext("too many arguments\n")); 1349 usage(B_FALSE); 1350 } 1351 } else { 1352 if (argc > 2) { 1353 (void) fprintf(stderr, gettext("too many arguments\n")); 1354 usage(B_FALSE); 1355 } 1356 1357 /* 1358 * Check for the SYS_CONFIG privilege. We do this explicitly 1359 * here because otherwise any attempt to discover pools will 1360 * silently fail. 1361 */ 1362 if (argc == 0 && !priv_ineffect(PRIV_SYS_CONFIG)) { 1363 (void) fprintf(stderr, gettext("cannot " 1364 "discover pools: permission denied\n")); 1365 free(searchdirs); 1366 return (1); 1367 } 1368 } 1369 1370 if ((pools = zpool_find_import(g_zfs, nsearch, searchdirs)) == NULL) { 1371 free(searchdirs); 1372 return (1); 1373 } 1374 1375 /* 1376 * We now have a list of all available pools in the given directories. 1377 * Depending on the arguments given, we do one of the following: 1378 * 1379 * <none> Iterate through all pools and display information about 1380 * each one. 1381 * 1382 * -a Iterate through all pools and try to import each one. 1383 * 1384 * <id> Find the pool that corresponds to the given GUID/pool 1385 * name and import that one. 1386 * 1387 * -D Above options applies only to destroyed pools. 1388 */ 1389 if (argc != 0) { 1390 char *endptr; 1391 1392 errno = 0; 1393 searchguid = strtoull(argv[0], &endptr, 10); 1394 if (errno != 0 || *endptr != '\0') 1395 searchname = argv[0]; 1396 else 1397 searchname = NULL; 1398 found_config = NULL; 1399 } 1400 1401 err = 0; 1402 elem = NULL; 1403 first = B_TRUE; 1404 while ((elem = nvlist_next_nvpair(pools, elem)) != NULL) { 1405 1406 verify(nvpair_value_nvlist(elem, &config) == 0); 1407 1408 verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_STATE, 1409 &pool_state) == 0); 1410 if (!do_destroyed && pool_state == POOL_STATE_DESTROYED) 1411 continue; 1412 if (do_destroyed && pool_state != POOL_STATE_DESTROYED) 1413 continue; 1414 1415 if (argc == 0) { 1416 if (first) 1417 first = B_FALSE; 1418 else if (!do_all) 1419 (void) printf("\n"); 1420 1421 if (do_all) 1422 err |= do_import(config, NULL, mntopts, 1423 altroot, do_force, argc + optind, 1424 argv - optind); 1425 else 1426 show_import(config); 1427 } else if (searchname != NULL) { 1428 char *name; 1429 1430 /* 1431 * We are searching for a pool based on name. 1432 */ 1433 verify(nvlist_lookup_string(config, 1434 ZPOOL_CONFIG_POOL_NAME, &name) == 0); 1435 1436 if (strcmp(name, searchname) == 0) { 1437 if (found_config != NULL) { 1438 (void) fprintf(stderr, gettext( 1439 "cannot import '%s': more than " 1440 "one matching pool\n"), searchname); 1441 (void) fprintf(stderr, gettext( 1442 "import by numeric ID instead\n")); 1443 err = B_TRUE; 1444 } 1445 found_config = config; 1446 } 1447 } else { 1448 uint64_t guid; 1449 1450 /* 1451 * Search for a pool by guid. 1452 */ 1453 verify(nvlist_lookup_uint64(config, 1454 ZPOOL_CONFIG_POOL_GUID, &guid) == 0); 1455 1456 if (guid == searchguid) 1457 found_config = config; 1458 } 1459 } 1460 1461 /* 1462 * If we were searching for a specific pool, verify that we found a 1463 * pool, and then do the import. 1464 */ 1465 if (argc != 0 && err == 0) { 1466 if (found_config == NULL) { 1467 (void) fprintf(stderr, gettext("cannot import '%s': " 1468 "no such pool available\n"), argv[0]); 1469 err = B_TRUE; 1470 } else { 1471 err |= do_import(found_config, argc == 1 ? NULL : 1472 argv[1], mntopts, altroot, do_force, argc + optind, 1473 argv - optind); 1474 } 1475 } 1476 1477 /* 1478 * If we were just looking for pools, report an error if none were 1479 * found. 1480 */ 1481 if (argc == 0 && first) 1482 (void) fprintf(stderr, 1483 gettext("no pools available to import\n")); 1484 1485 nvlist_free(pools); 1486 free(searchdirs); 1487 1488 return (err ? 1 : 0); 1489} 1490 1491typedef struct iostat_cbdata { 1492 zpool_list_t *cb_list; 1493 int cb_verbose; 1494 int cb_iteration; 1495 int cb_namewidth; 1496} iostat_cbdata_t; 1497 1498static void 1499print_iostat_separator(iostat_cbdata_t *cb) 1500{ 1501 int i = 0; 1502 1503 for (i = 0; i < cb->cb_namewidth; i++) 1504 (void) printf("-"); 1505 (void) printf(" ----- ----- ----- ----- ----- -----\n"); 1506} 1507 1508static void 1509print_iostat_header(iostat_cbdata_t *cb) 1510{ 1511 (void) printf("%*s capacity operations bandwidth\n", 1512 cb->cb_namewidth, ""); 1513 (void) printf("%-*s used avail read write read write\n", 1514 cb->cb_namewidth, "pool"); 1515 print_iostat_separator(cb); 1516} 1517 1518/* 1519 * Display a single statistic. 1520 */ 1521void 1522print_one_stat(uint64_t value) 1523{ 1524 char buf[64]; 1525 1526 zfs_nicenum(value, buf, sizeof (buf)); 1527 (void) printf(" %5s", buf); 1528} 1529 1530/* 1531 * Print out all the statistics for the given vdev. This can either be the 1532 * toplevel configuration, or called recursively. If 'name' is NULL, then this 1533 * is a verbose output, and we don't want to display the toplevel pool stats. 1534 */ 1535void 1536print_vdev_stats(zpool_handle_t *zhp, const char *name, nvlist_t *oldnv, 1537 nvlist_t *newnv, iostat_cbdata_t *cb, int depth) 1538{ 1539 nvlist_t **oldchild, **newchild; 1540 uint_t c, children; 1541 vdev_stat_t *oldvs, *newvs; 1542 vdev_stat_t zerovs = { 0 }; 1543 uint64_t tdelta; 1544 double scale; 1545 char *vname; 1546 1547 if (oldnv != NULL) { 1548 verify(nvlist_lookup_uint64_array(oldnv, ZPOOL_CONFIG_STATS, 1549 (uint64_t **)&oldvs, &c) == 0); 1550 } else { 1551 oldvs = &zerovs; 1552 } 1553 1554 verify(nvlist_lookup_uint64_array(newnv, ZPOOL_CONFIG_STATS, 1555 (uint64_t **)&newvs, &c) == 0); 1556 1557 if (strlen(name) + depth > cb->cb_namewidth) 1558 (void) printf("%*s%s", depth, "", name); 1559 else 1560 (void) printf("%*s%s%*s", depth, "", name, 1561 (int)(cb->cb_namewidth - strlen(name) - depth), ""); 1562 1563 tdelta = newvs->vs_timestamp - oldvs->vs_timestamp; 1564 1565 if (tdelta == 0) 1566 scale = 1.0; 1567 else 1568 scale = (double)NANOSEC / tdelta; 1569 1570 /* only toplevel vdevs have capacity stats */ 1571 if (newvs->vs_space == 0) { 1572 (void) printf(" - -"); 1573 } else { 1574 print_one_stat(newvs->vs_alloc); 1575 print_one_stat(newvs->vs_space - newvs->vs_alloc); 1576 } 1577 1578 print_one_stat((uint64_t)(scale * (newvs->vs_ops[ZIO_TYPE_READ] - 1579 oldvs->vs_ops[ZIO_TYPE_READ]))); 1580 1581 print_one_stat((uint64_t)(scale * (newvs->vs_ops[ZIO_TYPE_WRITE] - 1582 oldvs->vs_ops[ZIO_TYPE_WRITE]))); 1583 1584 print_one_stat((uint64_t)(scale * (newvs->vs_bytes[ZIO_TYPE_READ] - 1585 oldvs->vs_bytes[ZIO_TYPE_READ]))); 1586 1587 print_one_stat((uint64_t)(scale * (newvs->vs_bytes[ZIO_TYPE_WRITE] - 1588 oldvs->vs_bytes[ZIO_TYPE_WRITE]))); 1589 1590 (void) printf("\n"); 1591 1592 if (!cb->cb_verbose) 1593 return; 1594 1595 if (nvlist_lookup_nvlist_array(newnv, ZPOOL_CONFIG_CHILDREN, 1596 &newchild, &children) != 0) 1597 return; 1598 1599 if (oldnv && nvlist_lookup_nvlist_array(oldnv, ZPOOL_CONFIG_CHILDREN, 1600 &oldchild, &c) != 0) 1601 return; 1602 1603 for (c = 0; c < children; c++) { 1604 vname = zpool_vdev_name(g_zfs, zhp, newchild[c]); 1605 print_vdev_stats(zhp, vname, oldnv ? oldchild[c] : NULL, 1606 newchild[c], cb, depth + 2); 1607 free(vname); 1608 } 1609} 1610 1611static int 1612refresh_iostat(zpool_handle_t *zhp, void *data) 1613{ 1614 iostat_cbdata_t *cb = data; 1615 boolean_t missing; 1616 1617 /* 1618 * If the pool has disappeared, remove it from the list and continue. 1619 */ 1620 if (zpool_refresh_stats(zhp, &missing) != 0) 1621 return (-1); 1622 1623 if (missing) 1624 pool_list_remove(cb->cb_list, zhp); 1625 1626 return (0); 1627} 1628 1629/* 1630 * Callback to print out the iostats for the given pool. 1631 */ 1632int 1633print_iostat(zpool_handle_t *zhp, void *data) 1634{ 1635 iostat_cbdata_t *cb = data; 1636 nvlist_t *oldconfig, *newconfig; 1637 nvlist_t *oldnvroot, *newnvroot; 1638 1639 newconfig = zpool_get_config(zhp, &oldconfig); 1640 1641 if (cb->cb_iteration == 1) 1642 oldconfig = NULL; 1643 1644 verify(nvlist_lookup_nvlist(newconfig, ZPOOL_CONFIG_VDEV_TREE, 1645 &newnvroot) == 0); 1646 1647 if (oldconfig == NULL) 1648 oldnvroot = NULL; 1649 else 1650 verify(nvlist_lookup_nvlist(oldconfig, ZPOOL_CONFIG_VDEV_TREE, 1651 &oldnvroot) == 0); 1652 1653 /* 1654 * Print out the statistics for the pool. 1655 */ 1656 print_vdev_stats(zhp, zpool_get_name(zhp), oldnvroot, newnvroot, cb, 0); 1657 1658 if (cb->cb_verbose) 1659 print_iostat_separator(cb); 1660 1661 return (0); 1662} 1663 1664int 1665get_namewidth(zpool_handle_t *zhp, void *data) 1666{ 1667 iostat_cbdata_t *cb = data; 1668 nvlist_t *config, *nvroot; 1669 1670 if ((config = zpool_get_config(zhp, NULL)) != NULL) { 1671 verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, 1672 &nvroot) == 0); 1673 if (!cb->cb_verbose) 1674 cb->cb_namewidth = strlen(zpool_get_name(zhp)); 1675 else 1676 cb->cb_namewidth = max_width(zhp, nvroot, 0, 0); 1677 } 1678 1679 /* 1680 * The width must fall into the range [10,38]. The upper limit is the 1681 * maximum we can have and still fit in 80 columns. 1682 */ 1683 if (cb->cb_namewidth < 10) 1684 cb->cb_namewidth = 10; 1685 if (cb->cb_namewidth > 38) 1686 cb->cb_namewidth = 38; 1687 1688 return (0); 1689} 1690 1691/* 1692 * zpool iostat [-v] [pool] ... [interval [count]] 1693 * 1694 * -v Display statistics for individual vdevs 1695 * 1696 * This command can be tricky because we want to be able to deal with pool 1697 * creation/destruction as well as vdev configuration changes. The bulk of this 1698 * processing is handled by the pool_list_* routines in zpool_iter.c. We rely 1699 * on pool_list_update() to detect the addition of new pools. Configuration 1700 * changes are all handled within libzfs. 1701 */ 1702int 1703zpool_do_iostat(int argc, char **argv) 1704{ 1705 int c; 1706 int ret; 1707 int npools; 1708 unsigned long interval = 0, count = 0; 1709 zpool_list_t *list; 1710 boolean_t verbose = B_FALSE; 1711 iostat_cbdata_t cb; 1712 1713 /* check options */ 1714 while ((c = getopt(argc, argv, "v")) != -1) { 1715 switch (c) { 1716 case 'v': 1717 verbose = B_TRUE; 1718 break; 1719 case '?': 1720 (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1721 optopt); 1722 usage(B_FALSE); 1723 } 1724 } 1725 1726 argc -= optind; 1727 argv += optind; 1728 1729 /* 1730 * Determine if the last argument is an integer or a pool name 1731 */ 1732 if (argc > 0 && isdigit(argv[argc - 1][0])) { 1733 char *end; 1734 1735 errno = 0; 1736 interval = strtoul(argv[argc - 1], &end, 10); 1737 1738 if (*end == '\0' && errno == 0) { 1739 if (interval == 0) { 1740 (void) fprintf(stderr, gettext("interval " 1741 "cannot be zero\n")); 1742 usage(B_FALSE); 1743 } 1744 1745 /* 1746 * Ignore the last parameter 1747 */ 1748 argc--; 1749 } else { 1750 /* 1751 * If this is not a valid number, just plow on. The 1752 * user will get a more informative error message later 1753 * on. 1754 */ 1755 interval = 0; 1756 } 1757 } 1758 1759 /* 1760 * If the last argument is also an integer, then we have both a count 1761 * and an integer. 1762 */ 1763 if (argc > 0 && isdigit(argv[argc - 1][0])) { 1764 char *end; 1765 1766 errno = 0; 1767 count = interval; 1768 interval = strtoul(argv[argc - 1], &end, 10); 1769 1770 if (*end == '\0' && errno == 0) { 1771 if (interval == 0) { 1772 (void) fprintf(stderr, gettext("interval " 1773 "cannot be zero\n")); 1774 usage(B_FALSE); 1775 } 1776 1777 /* 1778 * Ignore the last parameter 1779 */ 1780 argc--; 1781 } else { 1782 interval = 0; 1783 } 1784 } 1785 1786 /* 1787 * Construct the list of all interesting pools. 1788 */ 1789 ret = 0; 1790 if ((list = pool_list_get(argc, argv, NULL, &ret)) == NULL) 1791 return (1); 1792 1793 if (pool_list_count(list) == 0 && argc != 0) { 1794 pool_list_free(list); 1795 return (1); 1796 } 1797 1798 if (pool_list_count(list) == 0 && interval == 0) { 1799 pool_list_free(list); 1800 (void) fprintf(stderr, gettext("no pools available\n")); 1801 return (1); 1802 } 1803 1804 /* 1805 * Enter the main iostat loop. 1806 */ 1807 cb.cb_list = list; 1808 cb.cb_verbose = verbose; 1809 cb.cb_iteration = 0; 1810 cb.cb_namewidth = 0; 1811 1812 for (;;) { 1813 pool_list_update(list); 1814 1815 if ((npools = pool_list_count(list)) == 0) 1816 break; 1817 1818 /* 1819 * Refresh all statistics. This is done as an explicit step 1820 * before calculating the maximum name width, so that any 1821 * configuration changes are properly accounted for. 1822 */ 1823 (void) pool_list_iter(list, B_FALSE, refresh_iostat, &cb); 1824 1825 /* 1826 * Iterate over all pools to determine the maximum width 1827 * for the pool / device name column across all pools. 1828 */ 1829 cb.cb_namewidth = 0; 1830 (void) pool_list_iter(list, B_FALSE, get_namewidth, &cb); 1831 1832 /* 1833 * If it's the first time, or verbose mode, print the header. 1834 */ 1835 if (++cb.cb_iteration == 1 || verbose) 1836 print_iostat_header(&cb); 1837 1838 (void) pool_list_iter(list, B_FALSE, print_iostat, &cb); 1839 1840 /* 1841 * If there's more than one pool, and we're not in verbose mode 1842 * (which prints a separator for us), then print a separator. 1843 */ 1844 if (npools > 1 && !verbose) 1845 print_iostat_separator(&cb); 1846 1847 if (verbose) 1848 (void) printf("\n"); 1849 1850 /* 1851 * Flush the output so that redirection to a file isn't buffered 1852 * indefinitely. 1853 */ 1854 (void) fflush(stdout); 1855 1856 if (interval == 0) 1857 break; 1858 1859 if (count != 0 && --count == 0) 1860 break; 1861 1862 (void) sleep(interval); 1863 } 1864 1865 pool_list_free(list); 1866 1867 return (ret); 1868} 1869 1870typedef struct list_cbdata { 1871 boolean_t cb_scripted; 1872 boolean_t cb_first; 1873 int cb_fields[MAX_FIELDS]; 1874 int cb_fieldcount; 1875} list_cbdata_t; 1876 1877/* 1878 * Given a list of columns to display, output appropriate headers for each one. 1879 */ 1880void 1881print_header(int *fields, size_t count) 1882{ 1883 int i; 1884 column_def_t *col; 1885 const char *fmt; 1886 1887 for (i = 0; i < count; i++) { 1888 col = &column_table[fields[i]]; 1889 if (i != 0) 1890 (void) printf(" "); 1891 if (col->cd_justify == left_justify) 1892 fmt = "%-*s"; 1893 else 1894 fmt = "%*s"; 1895 1896 (void) printf(fmt, i == count - 1 ? strlen(col->cd_title) : 1897 col->cd_width, col->cd_title); 1898 } 1899 1900 (void) printf("\n"); 1901} 1902 1903int 1904list_callback(zpool_handle_t *zhp, void *data) 1905{ 1906 list_cbdata_t *cbp = data; 1907 nvlist_t *config; 1908 int i; 1909 char buf[ZPOOL_MAXNAMELEN]; 1910 uint64_t total; 1911 uint64_t used; 1912 const char *fmt; 1913 column_def_t *col; 1914 1915 if (cbp->cb_first) { 1916 if (!cbp->cb_scripted) 1917 print_header(cbp->cb_fields, cbp->cb_fieldcount); 1918 cbp->cb_first = B_FALSE; 1919 } 1920 1921 if (zpool_get_state(zhp) == POOL_STATE_UNAVAIL) { 1922 config = NULL; 1923 } else { 1924 config = zpool_get_config(zhp, NULL); 1925 total = zpool_get_space_total(zhp); 1926 used = zpool_get_space_used(zhp); 1927 } 1928 1929 for (i = 0; i < cbp->cb_fieldcount; i++) { 1930 if (i != 0) { 1931 if (cbp->cb_scripted) 1932 (void) printf("\t"); 1933 else 1934 (void) printf(" "); 1935 } 1936 1937 col = &column_table[cbp->cb_fields[i]]; 1938 1939 switch (cbp->cb_fields[i]) { 1940 case ZPOOL_FIELD_NAME: 1941 (void) strlcpy(buf, zpool_get_name(zhp), sizeof (buf)); 1942 break; 1943 1944 case ZPOOL_FIELD_SIZE: 1945 if (config == NULL) 1946 (void) strlcpy(buf, "-", sizeof (buf)); 1947 else 1948 zfs_nicenum(total, buf, sizeof (buf)); 1949 break; 1950 1951 case ZPOOL_FIELD_USED: 1952 if (config == NULL) 1953 (void) strlcpy(buf, "-", sizeof (buf)); 1954 else 1955 zfs_nicenum(used, buf, sizeof (buf)); 1956 break; 1957 1958 case ZPOOL_FIELD_AVAILABLE: 1959 if (config == NULL) 1960 (void) strlcpy(buf, "-", sizeof (buf)); 1961 else 1962 zfs_nicenum(total - used, buf, sizeof (buf)); 1963 break; 1964 1965 case ZPOOL_FIELD_CAPACITY: 1966 if (config == NULL) { 1967 (void) strlcpy(buf, "-", sizeof (buf)); 1968 } else { 1969 uint64_t capacity = (total == 0 ? 0 : 1970 (used * 100 / total)); 1971 (void) snprintf(buf, sizeof (buf), "%llu%%", 1972 (u_longlong_t)capacity); 1973 } 1974 break; 1975 1976 case ZPOOL_FIELD_HEALTH: 1977 if (config == NULL) { 1978 (void) strlcpy(buf, "FAULTED", sizeof (buf)); 1979 } else { 1980 nvlist_t *nvroot; 1981 vdev_stat_t *vs; 1982 uint_t vsc; 1983 1984 verify(nvlist_lookup_nvlist(config, 1985 ZPOOL_CONFIG_VDEV_TREE, &nvroot) == 0); 1986 verify(nvlist_lookup_uint64_array(nvroot, 1987 ZPOOL_CONFIG_STATS, (uint64_t **)&vs, 1988 &vsc) == 0); 1989 (void) strlcpy(buf, state_to_name(vs), 1990 sizeof (buf)); 1991 } 1992 break; 1993 1994 case ZPOOL_FIELD_ROOT: 1995 if (config == NULL) 1996 (void) strlcpy(buf, "-", sizeof (buf)); 1997 else if (zpool_get_root(zhp, buf, sizeof (buf)) != 0) 1998 (void) strlcpy(buf, "-", sizeof (buf)); 1999 break; 2000 } 2001 2002 if (cbp->cb_scripted) 2003 (void) printf("%s", buf); 2004 else { 2005 if (col->cd_justify == left_justify) 2006 fmt = "%-*s"; 2007 else 2008 fmt = "%*s"; 2009 2010 (void) printf(fmt, i == cbp->cb_fieldcount - 1 ? 2011 strlen(buf) : col->cd_width, buf); 2012 } 2013 } 2014 2015 (void) printf("\n"); 2016 2017 return (0); 2018} 2019 2020/* 2021 * zpool list [-H] [-o field[,field]*] [pool] ... 2022 * 2023 * -H Scripted mode. Don't display headers, and separate fields by 2024 * a single tab. 2025 * -o List of fields to display. Defaults to all fields, or 2026 * "name,size,used,available,capacity,health,root" 2027 * 2028 * List all pools in the system, whether or not they're healthy. Output space 2029 * statistics for each one, as well as health status summary. 2030 */ 2031int 2032zpool_do_list(int argc, char **argv) 2033{ 2034 int c; 2035 int ret; 2036 list_cbdata_t cb = { 0 }; 2037 static char default_fields[] = 2038 "name,size,used,available,capacity,health,root"; 2039 char *fields = default_fields; 2040 char *value; 2041 2042 /* check options */ 2043 while ((c = getopt(argc, argv, ":Ho:")) != -1) { 2044 switch (c) { 2045 case 'H': 2046 cb.cb_scripted = B_TRUE; 2047 break; 2048 case 'o': 2049 fields = optarg; 2050 break; 2051 case ':': 2052 (void) fprintf(stderr, gettext("missing argument for " 2053 "'%c' option\n"), optopt); 2054 usage(B_FALSE); 2055 break; 2056 case '?': 2057 (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2058 optopt); 2059 usage(B_FALSE); 2060 } 2061 } 2062 2063 argc -= optind; 2064 argv += optind; 2065 2066 while (*fields != '\0') { 2067 if (cb.cb_fieldcount == MAX_FIELDS) { 2068 (void) fprintf(stderr, gettext("too many " 2069 "properties given to -o option\n")); 2070 usage(B_FALSE); 2071 } 2072 2073 if ((cb.cb_fields[cb.cb_fieldcount] = getsubopt(&fields, 2074 column_subopts, &value)) == -1) { 2075 (void) fprintf(stderr, gettext("invalid property " 2076 "'%s'\n"), value); 2077 usage(B_FALSE); 2078 } 2079 2080 cb.cb_fieldcount++; 2081 } 2082 2083 2084 cb.cb_first = B_TRUE; 2085 2086 ret = for_each_pool(argc, argv, B_TRUE, NULL, list_callback, &cb); 2087 2088 if (argc == 0 && cb.cb_first) { 2089 (void) printf(gettext("no pools available\n")); 2090 return (0); 2091 } 2092 2093 return (ret); 2094} 2095 2096static nvlist_t * 2097zpool_get_vdev_by_name(nvlist_t *nv, char *name) 2098{ 2099 nvlist_t **child; 2100 uint_t c, children; 2101 nvlist_t *match; 2102 char *path; 2103 2104 if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN, 2105 &child, &children) != 0) { 2106 verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path) == 0); 2107 if (strncmp(name, _PATH_DEV, sizeof(_PATH_DEV)-1) == 0) 2108 name += sizeof(_PATH_DEV)-1; 2109 if (strncmp(path, _PATH_DEV, sizeof(_PATH_DEV)-1) == 0) 2110 path += sizeof(_PATH_DEV)-1; 2111 if (strcmp(name, path) == 0) 2112 return (nv); 2113 return (NULL); 2114 } 2115 2116 for (c = 0; c < children; c++) 2117 if ((match = zpool_get_vdev_by_name(child[c], name)) != NULL) 2118 return (match); 2119 2120 return (NULL); 2121} 2122 2123static int 2124zpool_do_attach_or_replace(int argc, char **argv, int replacing) 2125{ 2126 boolean_t force = B_FALSE; 2127 int c; 2128 nvlist_t *nvroot; 2129 char *poolname, *old_disk, *new_disk; 2130 zpool_handle_t *zhp; 2131 nvlist_t *config; 2132 int ret; 2133 int log_argc; 2134 char **log_argv; 2135 2136 /* check options */ 2137 while ((c = getopt(argc, argv, "f")) != -1) { 2138 switch (c) { 2139 case 'f': 2140 force = B_TRUE; 2141 break; 2142 case '?': 2143 (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2144 optopt); 2145 usage(B_FALSE); 2146 } 2147 } 2148 2149 log_argc = argc; 2150 log_argv = argv; 2151 argc -= optind; 2152 argv += optind; 2153 2154 /* get pool name and check number of arguments */ 2155 if (argc < 1) { 2156 (void) fprintf(stderr, gettext("missing pool name argument\n")); 2157 usage(B_FALSE); 2158 } 2159 2160 poolname = argv[0]; 2161 2162 if (argc < 2) { 2163 (void) fprintf(stderr, 2164 gettext("missing <device> specification\n")); 2165 usage(B_FALSE); 2166 } 2167 2168 old_disk = argv[1]; 2169 2170 if (argc < 3) { 2171 if (!replacing) { 2172 (void) fprintf(stderr, 2173 gettext("missing <new_device> specification\n")); 2174 usage(B_FALSE); 2175 } 2176 new_disk = old_disk; 2177 argc -= 1; 2178 argv += 1; 2179 } else { 2180 new_disk = argv[2]; 2181 argc -= 2; 2182 argv += 2; 2183 } 2184 2185 if (argc > 1) { 2186 (void) fprintf(stderr, gettext("too many arguments\n")); 2187 usage(B_FALSE); 2188 } 2189 2190 if ((zhp = zpool_open(g_zfs, poolname)) == NULL) 2191 return (1); 2192 2193 if ((config = zpool_get_config(zhp, NULL)) == NULL) { 2194 (void) fprintf(stderr, gettext("pool '%s' is unavailable\n"), 2195 poolname); 2196 zpool_close(zhp); 2197 return (1); 2198 } 2199 2200 nvroot = make_root_vdev(config, force, B_FALSE, replacing, argc, argv); 2201 if (nvroot == NULL) { 2202 zpool_close(zhp); 2203 return (1); 2204 } 2205 2206 ret = zpool_vdev_attach(zhp, old_disk, new_disk, nvroot, replacing); 2207 2208 if (!ret) { 2209 zpool_log_history(g_zfs, log_argc, log_argv, poolname, B_TRUE, 2210 B_FALSE); 2211 } 2212 2213 nvlist_free(nvroot); 2214 zpool_close(zhp); 2215 2216 return (ret); 2217} 2218 2219/* 2220 * zpool replace [-f] <pool> <device> <new_device> 2221 * 2222 * -f Force attach, even if <new_device> appears to be in use. 2223 * 2224 * Replace <device> with <new_device>. 2225 */ 2226/* ARGSUSED */ 2227int 2228zpool_do_replace(int argc, char **argv) 2229{ 2230 return (zpool_do_attach_or_replace(argc, argv, B_TRUE)); 2231} 2232 2233/* 2234 * zpool attach [-f] <pool> <device> <new_device> 2235 * 2236 * -f Force attach, even if <new_device> appears to be in use. 2237 * 2238 * Attach <new_device> to the mirror containing <device>. If <device> is not 2239 * part of a mirror, then <device> will be transformed into a mirror of 2240 * <device> and <new_device>. In either case, <new_device> will begin life 2241 * with a DTL of [0, now], and will immediately begin to resilver itself. 2242 */ 2243int 2244zpool_do_attach(int argc, char **argv) 2245{ 2246 return (zpool_do_attach_or_replace(argc, argv, B_FALSE)); 2247} 2248 2249/* 2250 * zpool detach [-f] <pool> <device> 2251 * 2252 * -f Force detach of <device>, even if DTLs argue against it 2253 * (not supported yet) 2254 * 2255 * Detach a device from a mirror. The operation will be refused if <device> 2256 * is the last device in the mirror, or if the DTLs indicate that this device 2257 * has the only valid copy of some data. 2258 */ 2259/* ARGSUSED */ 2260int 2261zpool_do_detach(int argc, char **argv) 2262{ 2263 int c; 2264 char *poolname, *path; 2265 zpool_handle_t *zhp; 2266 int ret; 2267 2268 /* check options */ 2269 while ((c = getopt(argc, argv, "f")) != -1) { 2270 switch (c) { 2271 case 'f': 2272 case '?': 2273 (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2274 optopt); 2275 usage(B_FALSE); 2276 } 2277 } 2278 2279 argc -= optind; 2280 argv += optind; 2281 2282 /* get pool name and check number of arguments */ 2283 if (argc < 1) { 2284 (void) fprintf(stderr, gettext("missing pool name argument\n")); 2285 usage(B_FALSE); 2286 } 2287 2288 if (argc < 2) { 2289 (void) fprintf(stderr, 2290 gettext("missing <device> specification\n")); 2291 usage(B_FALSE); 2292 } 2293 2294 poolname = argv[0]; 2295 path = argv[1]; 2296 2297 if ((zhp = zpool_open(g_zfs, poolname)) == NULL) 2298 return (1); 2299 2300 ret = zpool_vdev_detach(zhp, path); 2301 2302 if (!ret) { 2303 zpool_log_history(g_zfs, argc + optind, argv - optind, poolname, 2304 B_TRUE, B_FALSE); 2305 } 2306 zpool_close(zhp); 2307 2308 return (ret); 2309} 2310 2311/* 2312 * zpool online <pool> <device> ... 2313 */ 2314/* ARGSUSED */ 2315int 2316zpool_do_online(int argc, char **argv) 2317{ 2318 int c, i; 2319 char *poolname; 2320 zpool_handle_t *zhp; 2321 int ret = 0; 2322 2323 /* check options */ 2324 while ((c = getopt(argc, argv, "t")) != -1) { 2325 switch (c) { 2326 case 't': 2327 case '?': 2328 (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2329 optopt); 2330 usage(B_FALSE); 2331 } 2332 } 2333 2334 argc -= optind; 2335 argv += optind; 2336 2337 /* get pool name and check number of arguments */ 2338 if (argc < 1) { 2339 (void) fprintf(stderr, gettext("missing pool name\n")); 2340 usage(B_FALSE); 2341 } 2342 if (argc < 2) { 2343 (void) fprintf(stderr, gettext("missing device name\n")); 2344 usage(B_FALSE); 2345 } 2346 2347 poolname = argv[0]; 2348 2349 if ((zhp = zpool_open(g_zfs, poolname)) == NULL) 2350 return (1); 2351 2352 for (i = 1; i < argc; i++) 2353 if (zpool_vdev_online(zhp, argv[i]) == 0) 2354 (void) printf(gettext("Bringing device %s online\n"), 2355 argv[i]); 2356 else 2357 ret = 1; 2358 2359 if (!ret) { 2360 zpool_log_history(g_zfs, argc + optind, argv - optind, poolname, 2361 B_TRUE, B_FALSE); 2362 } 2363 zpool_close(zhp); 2364 2365 return (ret); 2366} 2367 2368/* 2369 * zpool offline [-ft] <pool> <device> ... 2370 * 2371 * -f Force the device into the offline state, even if doing 2372 * so would appear to compromise pool availability. 2373 * (not supported yet) 2374 * 2375 * -t Only take the device off-line temporarily. The offline 2376 * state will not be persistent across reboots. 2377 */ 2378/* ARGSUSED */ 2379int 2380zpool_do_offline(int argc, char **argv) 2381{ 2382 int c, i; 2383 char *poolname; 2384 zpool_handle_t *zhp; 2385 int ret = 0; 2386 boolean_t istmp = B_FALSE; 2387 2388 /* check options */ 2389 while ((c = getopt(argc, argv, "ft")) != -1) { 2390 switch (c) { 2391 case 't': 2392 istmp = B_TRUE; 2393 break; 2394 case 'f': 2395 case '?': 2396 (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2397 optopt); 2398 usage(B_FALSE); 2399 } 2400 } 2401 2402 argc -= optind; 2403 argv += optind; 2404 2405 /* get pool name and check number of arguments */ 2406 if (argc < 1) { 2407 (void) fprintf(stderr, gettext("missing pool name\n")); 2408 usage(B_FALSE); 2409 } 2410 if (argc < 2) { 2411 (void) fprintf(stderr, gettext("missing device name\n")); 2412 usage(B_FALSE); 2413 } 2414 2415 poolname = argv[0]; 2416 2417 if ((zhp = zpool_open(g_zfs, poolname)) == NULL) 2418 return (1); 2419 2420 for (i = 1; i < argc; i++) 2421 if (zpool_vdev_offline(zhp, argv[i], istmp) == 0) 2422 (void) printf(gettext("Bringing device %s offline\n"), 2423 argv[i]); 2424 else 2425 ret = 1; 2426 2427 if (!ret) { 2428 zpool_log_history(g_zfs, argc + optind, argv - optind, poolname, 2429 B_TRUE, B_FALSE); 2430 } 2431 zpool_close(zhp); 2432 2433 return (ret); 2434} 2435 2436/* 2437 * zpool clear <pool> [device] 2438 * 2439 * Clear all errors associated with a pool or a particular device. 2440 */ 2441int 2442zpool_do_clear(int argc, char **argv) 2443{ 2444 int ret = 0; 2445 zpool_handle_t *zhp; 2446 char *pool, *device; 2447 2448 if (argc < 2) { 2449 (void) fprintf(stderr, gettext("missing pool name\n")); 2450 usage(B_FALSE); 2451 } 2452 2453 if (argc > 3) { 2454 (void) fprintf(stderr, gettext("too many arguments\n")); 2455 usage(B_FALSE); 2456 } 2457 2458 pool = argv[1]; 2459 device = argc == 3 ? argv[2] : NULL; 2460 2461 if ((zhp = zpool_open(g_zfs, pool)) == NULL) 2462 return (1); 2463 2464 if (zpool_clear(zhp, device) != 0) 2465 ret = 1; 2466 2467 if (!ret) 2468 zpool_log_history(g_zfs, argc, argv, pool, B_TRUE, B_FALSE); 2469 zpool_close(zhp); 2470 2471 return (ret); 2472} 2473 2474typedef struct scrub_cbdata { 2475 int cb_type; 2476 int cb_argc; 2477 char **cb_argv; 2478} scrub_cbdata_t; 2479 2480int 2481scrub_callback(zpool_handle_t *zhp, void *data) 2482{ 2483 scrub_cbdata_t *cb = data; 2484 int err; 2485 2486 /* 2487 * Ignore faulted pools. 2488 */ 2489 if (zpool_get_state(zhp) == POOL_STATE_UNAVAIL) { 2490 (void) fprintf(stderr, gettext("cannot scrub '%s': pool is " 2491 "currently unavailable\n"), zpool_get_name(zhp)); 2492 return (1); 2493 } 2494 2495 err = zpool_scrub(zhp, cb->cb_type); 2496 2497 if (!err) { 2498 zpool_log_history(g_zfs, cb->cb_argc, cb->cb_argv, 2499 zpool_get_name(zhp), B_TRUE, B_FALSE); 2500 } 2501 2502 return (err != 0); 2503} 2504 2505/* 2506 * zpool scrub [-s] <pool> ... 2507 * 2508 * -s Stop. Stops any in-progress scrub. 2509 */ 2510int 2511zpool_do_scrub(int argc, char **argv) 2512{ 2513 int c; 2514 scrub_cbdata_t cb; 2515 2516 cb.cb_type = POOL_SCRUB_EVERYTHING; 2517 2518 /* check options */ 2519 while ((c = getopt(argc, argv, "s")) != -1) { 2520 switch (c) { 2521 case 's': 2522 cb.cb_type = POOL_SCRUB_NONE; 2523 break; 2524 case '?': 2525 (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2526 optopt); 2527 usage(B_FALSE); 2528 } 2529 } 2530 2531 cb.cb_argc = argc; 2532 cb.cb_argv = argv; 2533 argc -= optind; 2534 argv += optind; 2535 2536 if (argc < 1) { 2537 (void) fprintf(stderr, gettext("missing pool name argument\n")); 2538 usage(B_FALSE); 2539 } 2540 2541 return (for_each_pool(argc, argv, B_TRUE, NULL, scrub_callback, &cb)); 2542} 2543 2544typedef struct status_cbdata { 2545 int cb_count; 2546 boolean_t cb_allpools; 2547 boolean_t cb_verbose; 2548 boolean_t cb_explain; 2549 boolean_t cb_first; 2550} status_cbdata_t; 2551 2552/* 2553 * Print out detailed scrub status. 2554 */ 2555void 2556print_scrub_status(nvlist_t *nvroot) 2557{ 2558 vdev_stat_t *vs; 2559 uint_t vsc; 2560 time_t start, end, now; 2561 double fraction_done; 2562 uint64_t examined, total, minutes_left; 2563 char *scrub_type; 2564 2565 verify(nvlist_lookup_uint64_array(nvroot, ZPOOL_CONFIG_STATS, 2566 (uint64_t **)&vs, &vsc) == 0); 2567 2568 /* 2569 * If there's never been a scrub, there's not much to say. 2570 */ 2571 if (vs->vs_scrub_end == 0 && vs->vs_scrub_type == POOL_SCRUB_NONE) { 2572 (void) printf(gettext("none requested\n")); 2573 return; 2574 } 2575 2576 scrub_type = (vs->vs_scrub_type == POOL_SCRUB_RESILVER) ? 2577 "resilver" : "scrub"; 2578 2579 start = vs->vs_scrub_start; 2580 end = vs->vs_scrub_end; 2581 now = time(NULL); 2582 examined = vs->vs_scrub_examined; 2583 total = vs->vs_alloc; 2584 2585 if (end != 0) { 2586 (void) printf(gettext("%s %s with %llu errors on %s"), 2587 scrub_type, vs->vs_scrub_complete ? "completed" : "stopped", 2588 (u_longlong_t)vs->vs_scrub_errors, ctime(&end)); 2589 return; 2590 } 2591 2592 if (examined == 0) 2593 examined = 1; 2594 if (examined > total) 2595 total = examined; 2596 2597 fraction_done = (double)examined / total; 2598 minutes_left = (uint64_t)((now - start) * 2599 (1 - fraction_done) / fraction_done / 60); 2600 2601 (void) printf(gettext("%s in progress, %.2f%% done, %lluh%um to go\n"), 2602 scrub_type, 100 * fraction_done, 2603 (u_longlong_t)(minutes_left / 60), (uint_t)(minutes_left % 60)); 2604} 2605 2606typedef struct spare_cbdata { 2607 uint64_t cb_guid; 2608 zpool_handle_t *cb_zhp; 2609} spare_cbdata_t; 2610 2611static boolean_t 2612find_vdev(nvlist_t *nv, uint64_t search) 2613{ 2614 uint64_t guid; 2615 nvlist_t **child; 2616 uint_t c, children; 2617 2618 if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID, &guid) == 0 && 2619 search == guid) 2620 return (B_TRUE); 2621 2622 if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN, 2623 &child, &children) == 0) { 2624 for (c = 0; c < children; c++) 2625 if (find_vdev(child[c], search)) 2626 return (B_TRUE); 2627 } 2628 2629 return (B_FALSE); 2630} 2631 2632static int 2633find_spare(zpool_handle_t *zhp, void *data) 2634{ 2635 spare_cbdata_t *cbp = data; 2636 nvlist_t *config, *nvroot; 2637 2638 config = zpool_get_config(zhp, NULL); 2639 verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, 2640 &nvroot) == 0); 2641 2642 if (find_vdev(nvroot, cbp->cb_guid)) { 2643 cbp->cb_zhp = zhp; 2644 return (1); 2645 } 2646 2647 zpool_close(zhp); 2648 return (0); 2649} 2650 2651/* 2652 * Print out configuration state as requested by status_callback. 2653 */ 2654void 2655print_status_config(zpool_handle_t *zhp, const char *name, nvlist_t *nv, 2656 int namewidth, int depth, boolean_t isspare) 2657{ 2658 nvlist_t **child; 2659 uint_t c, children; 2660 vdev_stat_t *vs; 2661 char rbuf[6], wbuf[6], cbuf[6], repaired[7]; 2662 char *vname; 2663 uint64_t notpresent; 2664 spare_cbdata_t cb; 2665 const char *state; 2666 2667 verify(nvlist_lookup_uint64_array(nv, ZPOOL_CONFIG_STATS, 2668 (uint64_t **)&vs, &c) == 0); 2669 2670 if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN, 2671 &child, &children) != 0) 2672 children = 0; 2673 2674 state = state_to_name(vs); 2675 if (isspare) { 2676 /* 2677 * For hot spares, we use the terms 'INUSE' and 'AVAILABLE' for 2678 * online drives. 2679 */ 2680 if (vs->vs_aux == VDEV_AUX_SPARED) 2681 state = "INUSE"; 2682 else if (vs->vs_state == VDEV_STATE_HEALTHY) 2683 state = "AVAIL"; 2684 } 2685 2686 (void) printf("\t%*s%-*s %-8s", depth, "", namewidth - depth, 2687 name, state); 2688 2689 if (!isspare) { 2690 zfs_nicenum(vs->vs_read_errors, rbuf, sizeof (rbuf)); 2691 zfs_nicenum(vs->vs_write_errors, wbuf, sizeof (wbuf)); 2692 zfs_nicenum(vs->vs_checksum_errors, cbuf, sizeof (cbuf)); 2693 (void) printf(" %5s %5s %5s", rbuf, wbuf, cbuf); 2694 } 2695 2696 if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_NOT_PRESENT, 2697 ¬present) == 0) { 2698 char *path; 2699 verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path) == 0); 2700 (void) printf(" was %s", path); 2701 } else if (vs->vs_aux != 0) { 2702 (void) printf(" "); 2703 2704 switch (vs->vs_aux) { 2705 case VDEV_AUX_OPEN_FAILED: 2706 (void) printf(gettext("cannot open")); 2707 break; 2708 2709 case VDEV_AUX_BAD_GUID_SUM: 2710 (void) printf(gettext("missing device")); 2711 break; 2712 2713 case VDEV_AUX_NO_REPLICAS: 2714 (void) printf(gettext("insufficient replicas")); 2715 break; 2716 2717 case VDEV_AUX_VERSION_NEWER: 2718 (void) printf(gettext("newer version")); 2719 break; 2720 2721 case VDEV_AUX_SPARED: 2722 verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID, 2723 &cb.cb_guid) == 0); 2724 if (zpool_iter(g_zfs, find_spare, &cb) == 1) { 2725 if (strcmp(zpool_get_name(cb.cb_zhp), 2726 zpool_get_name(zhp)) == 0) 2727 (void) printf(gettext("currently in " 2728 "use")); 2729 else 2730 (void) printf(gettext("in use by " 2731 "pool '%s'"), 2732 zpool_get_name(cb.cb_zhp)); 2733 zpool_close(cb.cb_zhp); 2734 } else { 2735 (void) printf(gettext("currently in use")); 2736 } 2737 break; 2738 2739 default: 2740 (void) printf(gettext("corrupted data")); 2741 break; 2742 } 2743 } else if (vs->vs_scrub_repaired != 0 && children == 0) { 2744 /* 2745 * Report bytes resilvered/repaired on leaf devices. 2746 */ 2747 zfs_nicenum(vs->vs_scrub_repaired, repaired, sizeof (repaired)); 2748 (void) printf(gettext(" %s %s"), repaired, 2749 (vs->vs_scrub_type == POOL_SCRUB_RESILVER) ? 2750 "resilvered" : "repaired"); 2751 } 2752 2753 (void) printf("\n"); 2754 2755 for (c = 0; c < children; c++) { 2756 vname = zpool_vdev_name(g_zfs, zhp, child[c]); 2757 print_status_config(zhp, vname, child[c], 2758 namewidth, depth + 2, isspare); 2759 free(vname); 2760 } 2761} 2762 2763static void 2764print_error_log(zpool_handle_t *zhp) 2765{ 2766 nvlist_t *nverrlist; 2767 nvpair_t *elem; 2768 char *pathname; 2769 size_t len = MAXPATHLEN * 2; 2770 2771 if (zpool_get_errlog(zhp, &nverrlist) != 0) { 2772 (void) printf("errors: List of errors unavailable " 2773 "(insufficient privileges)\n"); 2774 return; 2775 } 2776 2777 (void) printf("errors: Permanent errors have been " 2778 "detected in the following files:\n\n"); 2779 2780 pathname = safe_malloc(len); 2781 elem = NULL; 2782 while ((elem = nvlist_next_nvpair(nverrlist, elem)) != NULL) { 2783 nvlist_t *nv; 2784 uint64_t dsobj, obj; 2785 2786 verify(nvpair_value_nvlist(elem, &nv) == 0); 2787 verify(nvlist_lookup_uint64(nv, ZPOOL_ERR_DATASET, 2788 &dsobj) == 0); 2789 verify(nvlist_lookup_uint64(nv, ZPOOL_ERR_OBJECT, 2790 &obj) == 0); 2791 zpool_obj_to_path(zhp, dsobj, obj, pathname, len); 2792 (void) printf("%7s %s\n", "", pathname); 2793 } 2794 free(pathname); 2795 nvlist_free(nverrlist); 2796} 2797 2798static void 2799print_spares(zpool_handle_t *zhp, nvlist_t **spares, uint_t nspares, 2800 int namewidth) 2801{ 2802 uint_t i; 2803 char *name; 2804 2805 if (nspares == 0) 2806 return; 2807 2808 (void) printf(gettext("\tspares\n")); 2809 2810 for (i = 0; i < nspares; i++) { 2811 name = zpool_vdev_name(g_zfs, zhp, spares[i]); 2812 print_status_config(zhp, name, spares[i], 2813 namewidth, 2, B_TRUE); 2814 free(name); 2815 } 2816} 2817 2818/* 2819 * Display a summary of pool status. Displays a summary such as: 2820 * 2821 * pool: tank 2822 * status: DEGRADED 2823 * reason: One or more devices ... 2824 * see: http://www.sun.com/msg/ZFS-xxxx-01 2825 * config: 2826 * mirror DEGRADED 2827 * c1t0d0 OK 2828 * c2t0d0 UNAVAIL 2829 * 2830 * When given the '-v' option, we print out the complete config. If the '-e' 2831 * option is specified, then we print out error rate information as well. 2832 */ 2833int 2834status_callback(zpool_handle_t *zhp, void *data) 2835{ 2836 status_cbdata_t *cbp = data; 2837 nvlist_t *config, *nvroot; 2838 char *msgid; 2839 int reason; 2840 const char *health; 2841 uint_t c; 2842 vdev_stat_t *vs; 2843 2844 config = zpool_get_config(zhp, NULL); 2845 reason = zpool_get_status(zhp, &msgid); 2846 2847 cbp->cb_count++; 2848 2849 /* 2850 * If we were given 'zpool status -x', only report those pools with 2851 * problems. 2852 */ 2853 if (reason == ZPOOL_STATUS_OK && cbp->cb_explain) { 2854 if (!cbp->cb_allpools) { 2855 (void) printf(gettext("pool '%s' is healthy\n"), 2856 zpool_get_name(zhp)); 2857 if (cbp->cb_first) 2858 cbp->cb_first = B_FALSE; 2859 } 2860 return (0); 2861 } 2862 2863 if (cbp->cb_first) 2864 cbp->cb_first = B_FALSE; 2865 else 2866 (void) printf("\n"); 2867 2868 verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, 2869 &nvroot) == 0); 2870 verify(nvlist_lookup_uint64_array(nvroot, ZPOOL_CONFIG_STATS, 2871 (uint64_t **)&vs, &c) == 0); 2872 health = state_to_name(vs); 2873 2874 (void) printf(gettext(" pool: %s\n"), zpool_get_name(zhp)); 2875 (void) printf(gettext(" state: %s\n"), health); 2876 2877 switch (reason) { 2878 case ZPOOL_STATUS_MISSING_DEV_R: 2879 (void) printf(gettext("status: One or more devices could not " 2880 "be opened. Sufficient replicas exist for\n\tthe pool to " 2881 "continue functioning in a degraded state.\n")); 2882 (void) printf(gettext("action: Attach the missing device and " 2883 "online it using 'zpool online'.\n")); 2884 break; 2885 2886 case ZPOOL_STATUS_MISSING_DEV_NR: 2887 (void) printf(gettext("status: One or more devices could not " 2888 "be opened. There are insufficient\n\treplicas for the " 2889 "pool to continue functioning.\n")); 2890 (void) printf(gettext("action: Attach the missing device and " 2891 "online it using 'zpool online'.\n")); 2892 break; 2893 2894 case ZPOOL_STATUS_CORRUPT_LABEL_R: 2895 (void) printf(gettext("status: One or more devices could not " 2896 "be used because the label is missing or\n\tinvalid. " 2897 "Sufficient replicas exist for the pool to continue\n\t" 2898 "functioning in a degraded state.\n")); 2899 (void) printf(gettext("action: Replace the device using " 2900 "'zpool replace'.\n")); 2901 break; 2902 2903 case ZPOOL_STATUS_CORRUPT_LABEL_NR: 2904 (void) printf(gettext("status: One or more devices could not " 2905 "be used because the label is missing \n\tor invalid. " 2906 "There are insufficient replicas for the pool to " 2907 "continue\n\tfunctioning.\n")); 2908 (void) printf(gettext("action: Destroy and re-create the pool " 2909 "from a backup source.\n")); 2910 break; 2911 2912 case ZPOOL_STATUS_FAILING_DEV: 2913 (void) printf(gettext("status: One or more devices has " 2914 "experienced an unrecoverable error. An\n\tattempt was " 2915 "made to correct the error. Applications are " 2916 "unaffected.\n")); 2917 (void) printf(gettext("action: Determine if the device needs " 2918 "to be replaced, and clear the errors\n\tusing " 2919 "'zpool clear' or replace the device with 'zpool " 2920 "replace'.\n")); 2921 break; 2922 2923 case ZPOOL_STATUS_OFFLINE_DEV: 2924 (void) printf(gettext("status: One or more devices has " 2925 "been taken offline by the administrator.\n\tSufficient " 2926 "replicas exist for the pool to continue functioning in " 2927 "a\n\tdegraded state.\n")); 2928 (void) printf(gettext("action: Online the device using " 2929 "'zpool online' or replace the device with\n\t'zpool " 2930 "replace'.\n")); 2931 break; 2932 2933 case ZPOOL_STATUS_RESILVERING: 2934 (void) printf(gettext("status: One or more devices is " 2935 "currently being resilvered. The pool will\n\tcontinue " 2936 "to function, possibly in a degraded state.\n")); 2937 (void) printf(gettext("action: Wait for the resilver to " 2938 "complete.\n")); 2939 break; 2940 2941 case ZPOOL_STATUS_CORRUPT_DATA: 2942 (void) printf(gettext("status: One or more devices has " 2943 "experienced an error resulting in data\n\tcorruption. " 2944 "Applications may be affected.\n")); 2945 (void) printf(gettext("action: Restore the file in question " 2946 "if possible. Otherwise restore the\n\tentire pool from " 2947 "backup.\n")); 2948 break; 2949 2950 case ZPOOL_STATUS_CORRUPT_POOL: 2951 (void) printf(gettext("status: The pool metadata is corrupted " 2952 "and the pool cannot be opened.\n")); 2953 (void) printf(gettext("action: Destroy and re-create the pool " 2954 "from a backup source.\n")); 2955 break; 2956 2957 case ZPOOL_STATUS_VERSION_OLDER: 2958 (void) printf(gettext("status: The pool is formatted using an " 2959 "older on-disk format. The pool can\n\tstill be used, but " 2960 "some features are unavailable.\n")); 2961 (void) printf(gettext("action: Upgrade the pool using 'zpool " 2962 "upgrade'. Once this is done, the\n\tpool will no longer " 2963 "be accessible on older software versions.\n")); 2964 break; 2965 2966 case ZPOOL_STATUS_VERSION_NEWER: 2967 (void) printf(gettext("status: The pool has been upgraded to a " 2968 "newer, incompatible on-disk version.\n\tThe pool cannot " 2969 "be accessed on this system.\n")); 2970 (void) printf(gettext("action: Access the pool from a system " 2971 "running more recent software, or\n\trestore the pool from " 2972 "backup.\n")); 2973 break; 2974 2975 default: 2976 /* 2977 * The remaining errors can't actually be generated, yet. 2978 */ 2979 assert(reason == ZPOOL_STATUS_OK); 2980 } 2981 2982 if (msgid != NULL) 2983 (void) printf(gettext(" see: http://www.sun.com/msg/%s\n"), 2984 msgid); 2985 2986 if (config != NULL) { 2987 int namewidth; 2988 uint64_t nerr; 2989 nvlist_t **spares; 2990 uint_t nspares; 2991 2992 2993 (void) printf(gettext(" scrub: ")); 2994 print_scrub_status(nvroot); 2995 2996 namewidth = max_width(zhp, nvroot, 0, 0); 2997 if (namewidth < 10) 2998 namewidth = 10; 2999 3000 (void) printf(gettext("config:\n\n")); 3001 (void) printf(gettext("\t%-*s %-8s %5s %5s %5s\n"), namewidth, 3002 "NAME", "STATE", "READ", "WRITE", "CKSUM"); 3003 print_status_config(zhp, zpool_get_name(zhp), nvroot, 3004 namewidth, 0, B_FALSE); 3005 3006 if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_SPARES, 3007 &spares, &nspares) == 0) 3008 print_spares(zhp, spares, nspares, namewidth); 3009 3010 if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_ERRCOUNT, 3011 &nerr) == 0) { 3012 nvlist_t *nverrlist = NULL; 3013 3014 /* 3015 * If the approximate error count is small, get a 3016 * precise count by fetching the entire log and 3017 * uniquifying the results. 3018 */ 3019 if (nerr < 100 && !cbp->cb_verbose && 3020 zpool_get_errlog(zhp, &nverrlist) == 0) { 3021 nvpair_t *elem; 3022 3023 elem = NULL; 3024 nerr = 0; 3025 while ((elem = nvlist_next_nvpair(nverrlist, 3026 elem)) != NULL) { 3027 nerr++; 3028 } 3029 } 3030 nvlist_free(nverrlist); 3031 3032 (void) printf("\n"); 3033 3034 if (nerr == 0) 3035 (void) printf(gettext("errors: No known data " 3036 "errors\n")); 3037 else if (!cbp->cb_verbose) 3038 (void) printf(gettext("errors: %llu data " 3039 "errors, use '-v' for a list\n"), 3040 (u_longlong_t)nerr); 3041 else 3042 print_error_log(zhp); 3043 } 3044 } else { 3045 (void) printf(gettext("config: The configuration cannot be " 3046 "determined.\n")); 3047 } 3048 3049 return (0); 3050} 3051 3052/* 3053 * zpool status [-vx] [pool] ... 3054 * 3055 * -v Display complete error logs 3056 * -x Display only pools with potential problems 3057 * 3058 * Describes the health status of all pools or some subset. 3059 */ 3060int 3061zpool_do_status(int argc, char **argv) 3062{ 3063 int c; 3064 int ret; 3065 status_cbdata_t cb = { 0 }; 3066 3067 /* check options */ 3068 while ((c = getopt(argc, argv, "vx")) != -1) { 3069 switch (c) { 3070 case 'v': 3071 cb.cb_verbose = B_TRUE; 3072 break; 3073 case 'x': 3074 cb.cb_explain = B_TRUE; 3075 break; 3076 case '?': 3077 (void) fprintf(stderr, gettext("invalid option '%c'\n"), 3078 optopt); 3079 usage(B_FALSE); 3080 } 3081 } 3082 3083 argc -= optind; 3084 argv += optind; 3085 3086 cb.cb_first = B_TRUE; 3087 3088 if (argc == 0) 3089 cb.cb_allpools = B_TRUE; 3090 3091 ret = for_each_pool(argc, argv, B_TRUE, NULL, status_callback, &cb); 3092 3093 if (argc == 0 && cb.cb_count == 0) 3094 (void) printf(gettext("no pools available\n")); 3095 else if (cb.cb_explain && cb.cb_first && cb.cb_allpools) 3096 (void) printf(gettext("all pools are healthy\n")); 3097 3098 return (ret); 3099} 3100 3101typedef struct upgrade_cbdata { 3102 int cb_all; 3103 int cb_first; 3104 int cb_newer; 3105 int cb_argc; 3106 char **cb_argv; 3107} upgrade_cbdata_t; 3108 3109static int 3110upgrade_cb(zpool_handle_t *zhp, void *arg) 3111{ 3112 upgrade_cbdata_t *cbp = arg; 3113 nvlist_t *config; 3114 uint64_t version; 3115 int ret = 0; 3116 3117 config = zpool_get_config(zhp, NULL); 3118 verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_VERSION, 3119 &version) == 0); 3120 3121 if (!cbp->cb_newer && version < ZFS_VERSION) { 3122 if (!cbp->cb_all) { 3123 if (cbp->cb_first) { 3124 (void) printf(gettext("The following pools are " 3125 "out of date, and can be upgraded. After " 3126 "being\nupgraded, these pools will no " 3127 "longer be accessible by older software " 3128 "versions.\n\n")); 3129 (void) printf(gettext("VER POOL\n")); 3130 (void) printf(gettext("--- ------------\n")); 3131 cbp->cb_first = B_FALSE; 3132 } 3133 3134 (void) printf("%2llu %s\n", (u_longlong_t)version, 3135 zpool_get_name(zhp)); 3136 } else { 3137 cbp->cb_first = B_FALSE; 3138 ret = zpool_upgrade(zhp); 3139 if (!ret) { 3140 zpool_log_history(g_zfs, cbp->cb_argc, 3141 cbp->cb_argv, zpool_get_name(zhp), B_TRUE, 3142 B_FALSE); 3143 (void) printf(gettext("Successfully upgraded " 3144 "'%s'\n"), zpool_get_name(zhp)); 3145 } 3146 } 3147 } else if (cbp->cb_newer && version > ZFS_VERSION) { 3148 assert(!cbp->cb_all); 3149 3150 if (cbp->cb_first) { 3151 (void) printf(gettext("The following pools are " 3152 "formatted using a newer software version and\n" 3153 "cannot be accessed on the current system.\n\n")); 3154 (void) printf(gettext("VER POOL\n")); 3155 (void) printf(gettext("--- ------------\n")); 3156 cbp->cb_first = B_FALSE; 3157 } 3158 3159 (void) printf("%2llu %s\n", (u_longlong_t)version, 3160 zpool_get_name(zhp)); 3161 } 3162 3163 zpool_close(zhp); 3164 return (ret); 3165} 3166 3167/* ARGSUSED */ 3168static int 3169upgrade_one(zpool_handle_t *zhp, void *data) 3170{ 3171 nvlist_t *config; 3172 uint64_t version; 3173 int ret; 3174 upgrade_cbdata_t *cbp = data; 3175 3176 config = zpool_get_config(zhp, NULL); 3177 verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_VERSION, 3178 &version) == 0); 3179 3180 if (version == ZFS_VERSION) { 3181 (void) printf(gettext("Pool '%s' is already formatted " 3182 "using the current version.\n"), zpool_get_name(zhp)); 3183 return (0); 3184 } 3185 3186 ret = zpool_upgrade(zhp); 3187 3188 if (!ret) { 3189 zpool_log_history(g_zfs, cbp->cb_argc, cbp->cb_argv, 3190 zpool_get_name(zhp), B_TRUE, B_FALSE); 3191 (void) printf(gettext("Successfully upgraded '%s' " 3192 "from version %llu to version %llu\n"), zpool_get_name(zhp), 3193 (u_longlong_t)version, (u_longlong_t)ZFS_VERSION); 3194 } 3195 3196 return (ret != 0); 3197} 3198 3199/* 3200 * zpool upgrade 3201 * zpool upgrade -v 3202 * zpool upgrade <-a | pool> 3203 * 3204 * With no arguments, display downrev'd ZFS pool available for upgrade. 3205 * Individual pools can be upgraded by specifying the pool, and '-a' will 3206 * upgrade all pools. 3207 */ 3208int 3209zpool_do_upgrade(int argc, char **argv) 3210{ 3211 int c; 3212 upgrade_cbdata_t cb = { 0 }; 3213 int ret = 0; 3214 boolean_t showversions = B_FALSE; 3215 3216 /* check options */ 3217 while ((c = getopt(argc, argv, "av")) != -1) { 3218 switch (c) { 3219 case 'a': 3220 cb.cb_all = B_TRUE; 3221 break; 3222 case 'v': 3223 showversions = B_TRUE; 3224 break; 3225 case '?': 3226 (void) fprintf(stderr, gettext("invalid option '%c'\n"), 3227 optopt); 3228 usage(B_FALSE); 3229 } 3230 } 3231 3232 cb.cb_argc = argc; 3233 cb.cb_argv = argv; 3234 argc -= optind; 3235 argv += optind; 3236 3237 if (showversions) { 3238 if (cb.cb_all || argc != 0) { 3239 (void) fprintf(stderr, gettext("-v option is " 3240 "incompatible with other arguments\n")); 3241 usage(B_FALSE); 3242 } 3243 } else if (cb.cb_all) { 3244 if (argc != 0) { 3245 (void) fprintf(stderr, gettext("-a option is " 3246 "incompatible with other arguments\n")); 3247 usage(B_FALSE); 3248 } 3249 } 3250 3251 (void) printf(gettext("This system is currently running ZFS version " 3252 "%llu.\n\n"), ZFS_VERSION); 3253 cb.cb_first = B_TRUE; 3254 if (showversions) { 3255 (void) printf(gettext("The following versions are " 3256 "supported:\n\n")); 3257 (void) printf(gettext("VER DESCRIPTION\n")); 3258 (void) printf("--- -----------------------------------------" 3259 "---------------\n"); 3260 (void) printf(gettext(" 1 Initial ZFS version\n")); 3261 (void) printf(gettext(" 2 Ditto blocks " 3262 "(replicated metadata)\n")); 3263 (void) printf(gettext(" 3 Hot spares and double parity " 3264 "RAID-Z\n")); 3265 (void) printf(gettext(" 4 zpool history\n")); 3266 (void) printf(gettext(" 5 Compression using the gzip " 3267 "algorithm\n")); 3268 (void) printf(gettext(" 6 bootfs pool property ")); 3269 (void) printf(gettext("\nFor more information on a particular " 3270 "version, including supported releases, see:\n\n")); 3271 (void) printf("http://www.opensolaris.org/os/community/zfs/" 3272 "version/N\n\n"); 3273 (void) printf(gettext("Where 'N' is the version number.\n")); 3274 } else if (argc == 0) { 3275 int notfound; 3276 3277 ret = zpool_iter(g_zfs, upgrade_cb, &cb); 3278 notfound = cb.cb_first; 3279 3280 if (!cb.cb_all && ret == 0) { 3281 if (!cb.cb_first) 3282 (void) printf("\n"); 3283 cb.cb_first = B_TRUE; 3284 cb.cb_newer = B_TRUE; 3285 ret = zpool_iter(g_zfs, upgrade_cb, &cb); 3286 if (!cb.cb_first) { 3287 notfound = B_FALSE; 3288 (void) printf("\n"); 3289 } 3290 } 3291 3292 if (ret == 0) { 3293 if (notfound) 3294 (void) printf(gettext("All pools are formatted " 3295 "using this version.\n")); 3296 else if (!cb.cb_all) 3297 (void) printf(gettext("Use 'zpool upgrade -v' " 3298 "for a list of available versions and " 3299 "their associated\nfeatures.\n")); 3300 } 3301 } else { 3302 ret = for_each_pool(argc, argv, B_FALSE, NULL, 3303 upgrade_one, &cb); 3304 } 3305 3306 return (ret); 3307} 3308 3309/* 3310 * Print out the command history for a specific pool. 3311 */ 3312static int 3313get_history_one(zpool_handle_t *zhp, void *data) 3314{ 3315 nvlist_t *nvhis; 3316 nvlist_t **records; 3317 uint_t numrecords; 3318 char *cmdstr; 3319 uint64_t dst_time; 3320 time_t tsec; 3321 struct tm t; 3322 char tbuf[30]; 3323 int ret, i; 3324 3325 *(boolean_t *)data = B_FALSE; 3326 3327 (void) printf(gettext("History for '%s':\n"), zpool_get_name(zhp)); 3328 3329 if ((ret = zpool_get_history(zhp, &nvhis)) != 0) 3330 return (ret); 3331 3332 verify(nvlist_lookup_nvlist_array(nvhis, ZPOOL_HIST_RECORD, 3333 &records, &numrecords) == 0); 3334 for (i = 0; i < numrecords; i++) { 3335 if (nvlist_lookup_uint64(records[i], ZPOOL_HIST_TIME, 3336 &dst_time) == 0) { 3337 verify(nvlist_lookup_string(records[i], ZPOOL_HIST_CMD, 3338 &cmdstr) == 0); 3339 tsec = dst_time; 3340 (void) localtime_r(&tsec, &t); 3341 (void) strftime(tbuf, sizeof (tbuf), "%F.%T", &t); 3342 (void) printf("%s %s\n", tbuf, cmdstr); 3343 } 3344 } 3345 (void) printf("\n"); 3346 nvlist_free(nvhis); 3347 3348 return (ret); 3349} 3350 3351/* 3352 * zpool history <pool> 3353 * 3354 * Displays the history of commands that modified pools. 3355 */ 3356int 3357zpool_do_history(int argc, char **argv) 3358{ 3359 boolean_t first = B_TRUE; 3360 int ret; 3361 3362 argc -= optind; 3363 argv += optind; 3364 3365 ret = for_each_pool(argc, argv, B_FALSE, NULL, get_history_one, 3366 &first); 3367 3368 if (argc == 0 && first == B_TRUE) { 3369 (void) printf(gettext("no pools available\n")); 3370 return (0); 3371 } 3372 3373 return (ret); 3374} 3375 3376static int 3377get_callback(zpool_handle_t *zhp, void *data) 3378{ 3379 libzfs_get_cbdata_t *cbp = (libzfs_get_cbdata_t *)data; 3380 char value[MAXNAMELEN]; 3381 zfs_source_t srctype; 3382 zpool_proplist_t *pl; 3383 3384 for (pl = cbp->cb_proplist; pl != NULL; pl = pl->pl_next) { 3385 3386 /* 3387 * Skip the special fake placeholder. 3388 */ 3389 if (pl->pl_prop == ZFS_PROP_NAME && 3390 pl == cbp->cb_proplist) 3391 continue; 3392 3393 if (zpool_get_prop(zhp, pl->pl_prop, 3394 value, sizeof (value), &srctype) != 0) 3395 continue; 3396 3397 libzfs_print_one_property(zpool_get_name(zhp), cbp, 3398 zpool_prop_to_name(pl->pl_prop), value, srctype, NULL); 3399 } 3400 return (0); 3401} 3402 3403int 3404zpool_do_get(int argc, char **argv) 3405{ 3406 libzfs_get_cbdata_t cb = { 0 }; 3407 zpool_proplist_t fake_name = { 0 }; 3408 int ret; 3409 3410 if (argc < 3) 3411 usage(B_FALSE); 3412 3413 cb.cb_first = B_TRUE; 3414 cb.cb_sources = ZFS_SRC_ALL; 3415 cb.cb_columns[0] = GET_COL_NAME; 3416 cb.cb_columns[1] = GET_COL_PROPERTY; 3417 cb.cb_columns[2] = GET_COL_VALUE; 3418 cb.cb_columns[3] = GET_COL_SOURCE; 3419 3420 if (zpool_get_proplist(g_zfs, argv[1], &cb.cb_proplist) != 0) 3421 usage(B_FALSE); 3422 3423 if (cb.cb_proplist != NULL) { 3424 fake_name.pl_prop = ZFS_PROP_NAME; 3425 fake_name.pl_width = strlen(gettext("NAME")); 3426 fake_name.pl_next = cb.cb_proplist; 3427 cb.cb_proplist = &fake_name; 3428 } 3429 3430 ret = for_each_pool(argc - 2, argv + 2, B_TRUE, &cb.cb_proplist, 3431 get_callback, &cb); 3432 3433 if (cb.cb_proplist == &fake_name) 3434 zfs_free_proplist(fake_name.pl_next); 3435 else 3436 zfs_free_proplist(cb.cb_proplist); 3437 3438 return (ret); 3439} 3440 3441typedef struct set_cbdata { 3442 char *cb_propname; 3443 char *cb_value; 3444 boolean_t cb_any_successful; 3445} set_cbdata_t; 3446 3447int 3448set_callback(zpool_handle_t *zhp, void *data) 3449{ 3450 int error; 3451 set_cbdata_t *cb = (set_cbdata_t *)data; 3452 3453 error = zpool_set_prop(zhp, cb->cb_propname, cb->cb_value); 3454 3455 if (!error) 3456 cb->cb_any_successful = B_TRUE; 3457 3458 return (error); 3459} 3460 3461int 3462zpool_do_set(int argc, char **argv) 3463{ 3464 set_cbdata_t cb = { 0 }; 3465 int error; 3466 3467 if (argc > 1 && argv[1][0] == '-') { 3468 (void) fprintf(stderr, gettext("invalid option '%c'\n"), 3469 argv[1][1]); 3470 usage(B_FALSE); 3471 } 3472 3473 if (argc < 2) { 3474 (void) fprintf(stderr, gettext("missing property=value " 3475 "argument\n")); 3476 usage(B_FALSE); 3477 } 3478 3479 if (argc < 3) { 3480 (void) fprintf(stderr, gettext("missing pool name\n")); 3481 usage(B_FALSE); 3482 } 3483 3484 if (argc > 3) { 3485 (void) fprintf(stderr, gettext("too many pool names\n")); 3486 usage(B_FALSE); 3487 } 3488 3489 cb.cb_propname = argv[1]; 3490 cb.cb_value = strchr(cb.cb_propname, '='); 3491 if (cb.cb_value == NULL) { 3492 (void) fprintf(stderr, gettext("missing value in " 3493 "property=value argument\n")); 3494 usage(B_FALSE); 3495 } 3496 3497 *(cb.cb_value) = '\0'; 3498 cb.cb_value++; 3499 3500 error = for_each_pool(argc - 2, argv + 2, B_TRUE, NULL, 3501 set_callback, &cb); 3502 3503 if (cb.cb_any_successful) { 3504 *(cb.cb_value - 1) = '='; 3505 zpool_log_history(g_zfs, argc, argv, argv[2], B_FALSE, B_FALSE); 3506 } 3507 3508 return (error); 3509} 3510 3511static int 3512find_command_idx(char *command, int *idx) 3513{ 3514 int i; 3515 3516 for (i = 0; i < NCOMMAND; i++) { 3517 if (command_table[i].name == NULL) 3518 continue; 3519 3520 if (strcmp(command, command_table[i].name) == 0) { 3521 *idx = i; 3522 return (0); 3523 } 3524 } 3525 return (1); 3526} 3527 3528int 3529main(int argc, char **argv) 3530{ 3531 int ret; 3532 int i; 3533 char *cmdname; 3534 int found = 0; 3535 3536 (void) setlocale(LC_ALL, ""); 3537 (void) textdomain(TEXT_DOMAIN); 3538 3539 if ((g_zfs = libzfs_init()) == NULL) { 3540 (void) fprintf(stderr, gettext("internal error: failed to " 3541 "initialize ZFS library\n")); 3542 return (1); 3543 } 3544 3545 libzfs_print_on_error(g_zfs, B_TRUE); 3546 3547 opterr = 0; 3548 3549 /* 3550 * Make sure the user has specified some command. 3551 */ 3552 if (argc < 2) { 3553 (void) fprintf(stderr, gettext("missing command\n")); 3554 usage(B_FALSE); 3555 } 3556 3557 cmdname = argv[1]; 3558 3559 /* 3560 * Special case '-?' 3561 */ 3562 if (strcmp(cmdname, "-?") == 0) 3563 usage(B_TRUE); 3564 3565 /* 3566 * Run the appropriate command. 3567 */ 3568 if (find_command_idx(cmdname, &i) == 0) { 3569 current_command = &command_table[i]; 3570 ret = command_table[i].func(argc - 1, argv + 1); 3571 found++; 3572 } 3573 3574 /* 3575 * 'freeze' is a vile debugging abomination, so we treat it as such. 3576 */ 3577 if (strcmp(cmdname, "freeze") == 0 && argc == 3) { 3578 char buf[16384]; 3579 int fd = open(ZFS_DEV, O_RDWR); 3580 (void) strcpy((void *)buf, argv[2]); 3581 return (!!ioctl(fd, ZFS_IOC_POOL_FREEZE, buf)); 3582 } 3583 3584 if (!found) { 3585 (void) fprintf(stderr, gettext("unrecognized " 3586 "command '%s'\n"), cmdname); 3587 usage(B_FALSE); 3588 } 3589 3590 libzfs_fini(g_zfs); 3591 3592 /* 3593 * The 'ZFS_ABORT' environment variable causes us to dump core on exit 3594 * for the purposes of running ::findleaks. 3595 */ 3596 if (getenv("ZFS_ABORT") != NULL) { 3597 (void) printf("dumping core by request\n"); 3598 abort(); 3599 } 3600 3601 return (ret); 3602} 3603