Add .zfs control directory
[zfs.git] / cmd / zpool / zpool_main.c
old mode 100644 (file)
new mode 100755 (executable)
index 62c4be8..9e73dda
@@ -42,6 +42,8 @@
 #include <zone.h>
 #include <sys/fs/zfs.h>
 #include <sys/stat.h>
+#include <sys/fm/util.h>
+#include <sys/fm/protocol.h>
 
 #include <libzfs.h>
 
@@ -77,6 +79,7 @@ static int zpool_do_export(int, char **);
 static int zpool_do_upgrade(int, char **);
 
 static int zpool_do_history(int, char **);
+static int zpool_do_events(int, char **);
 
 static int zpool_do_get(int, char **);
 static int zpool_do_set(int, char **);
@@ -119,6 +122,7 @@ typedef enum {
        HELP_SCRUB,
        HELP_STATUS,
        HELP_UPGRADE,
+       HELP_EVENTS,
        HELP_GET,
        HELP_SET,
        HELP_SPLIT
@@ -167,6 +171,8 @@ static zpool_command_t command_table[] = {
        { "upgrade",    zpool_do_upgrade,       HELP_UPGRADE            },
        { NULL },
        { "history",    zpool_do_history,       HELP_HISTORY            },
+       { "events",     zpool_do_events,        HELP_EVENTS             },
+       { NULL },
        { "get",        zpool_do_get,           HELP_GET                },
        { "set",        zpool_do_set,           HELP_SET                },
 };
@@ -202,12 +208,14 @@ get_usage(zpool_help_t idx) {
                return (gettext("\thistory [-il] [<pool>] ...\n"));
        case HELP_IMPORT:
                return (gettext("\timport [-d dir] [-D]\n"
-                   "\timport [-d dir | -c cachefile] [-n] -F <pool | id>\n"
+                   "\timport [-d dir | -c cachefile] [-F [-n]] <pool | id>\n"
                    "\timport [-o mntopts] [-o property=value] ... \n"
-                   "\t    [-d dir | -c cachefile] [-D] [-f] [-R root] -a\n"
+                   "\t    [-d dir | -c cachefile] [-D] [-f] [-m] [-N] "
+                   "[-R root] [-F [-n]] -a\n"
                    "\timport [-o mntopts] [-o property=value] ... \n"
-                   "\t    [-d dir | -c cachefile] [-D] [-f] [-R root] "
-                   "<pool | id> [newpool]\n"));
+                   "\t    [-d dir | -c cachefile] [-D] [-f] [-m] [-N] "
+                   "[-R root] [-F [-n]]\n"
+                   "\t    <pool | id> [newpool]\n"));
        case HELP_IOSTAT:
                return (gettext("\tiostat [-v] [-T d|u] [pool] ... [interval "
                    "[count]]\n"));
@@ -232,6 +240,8 @@ get_usage(zpool_help_t idx) {
                return (gettext("\tupgrade\n"
                    "\tupgrade -v\n"
                    "\tupgrade [-V version] <-a | pool ...>\n"));
+       case HELP_EVENTS:
+               return (gettext("\tevents [-vHfc]\n"));
        case HELP_GET:
                return (gettext("\tget <\"all\" | property[,...]> "
                    "<pool> ...\n"));
@@ -478,7 +488,7 @@ zpool_do_add(int argc, char **argv)
        }
 
        /* pass off to get_vdev_spec for processing */
-       nvroot = make_root_vdev(zhp, force, !force, B_FALSE, dryrun,
+       nvroot = make_root_vdev(zhp, NULL, force, !force, B_FALSE, dryrun,
            argc, argv);
        if (nvroot == NULL) {
                zpool_close(zhp);
@@ -678,7 +688,7 @@ zpool_do_create(int argc, char **argv)
        }
 
        /* pass off to get_vdev_spec for bulk processing */
-       nvroot = make_root_vdev(NULL, force, !force, B_FALSE, dryrun,
+       nvroot = make_root_vdev(NULL, props, force, !force, B_FALSE, dryrun,
            argc - 1, argv + 1);
        if (nvroot == NULL)
                goto errout;
@@ -1472,7 +1482,7 @@ show_import(nvlist_t *config)
        }
 
        if (msgid != NULL)
-               (void) printf(gettext("   see: http://www.sun.com/msg/%s\n"),
+               (void) printf(gettext("   see: http://zfsonlinux.org/msg/%s\n"),
                    msgid);
 
        (void) printf(gettext("config:\n\n"));
@@ -1499,7 +1509,7 @@ show_import(nvlist_t *config)
  */
 static int
 do_import(nvlist_t *config, const char *newname, const char *mntopts,
-    int force, nvlist_t *props, boolean_t do_verbatim)
+    nvlist_t *props, int flags)
 {
        zpool_handle_t *zhp;
        char *name;
@@ -1517,12 +1527,15 @@ do_import(nvlist_t *config, const char *newname, const char *mntopts,
                (void) fprintf(stderr, gettext("cannot import '%s': pool "
                    "is formatted using a newer ZFS version\n"), name);
                return (1);
-       } else if (state != POOL_STATE_EXPORTED && !force) {
+       } else if (state != POOL_STATE_EXPORTED &&
+           !(flags & ZFS_IMPORT_ANY_HOST)) {
                uint64_t hostid;
 
                if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_HOSTID,
                    &hostid) == 0) {
-                       if ((unsigned long)hostid != gethostid()) {
+                       unsigned long system_hostid = gethostid() & 0xffffffff;
+
+                       if ((unsigned long)hostid != system_hostid) {
                                char *hostname;
                                uint64_t timestamp;
                                time_t t;
@@ -1551,7 +1564,7 @@ do_import(nvlist_t *config, const char *newname, const char *mntopts,
                }
        }
 
-       if (zpool_import_props(g_zfs, config, newname, props, do_verbatim) != 0)
+       if (zpool_import_props(g_zfs, config, newname, props, flags) != 0)
                return (1);
 
        if (newname != NULL)
@@ -1561,6 +1574,7 @@ do_import(nvlist_t *config, const char *newname, const char *mntopts,
                return (1);
 
        if (zpool_get_state(zhp) != POOL_STATE_UNAVAIL &&
+           !(flags & ZFS_IMPORT_ONLY) &&
            zpool_enable_datasets(zhp, mntopts, 0) != 0) {
                zpool_close(zhp);
                return (1);
@@ -1580,7 +1594,7 @@ do_import(nvlist_t *config, const char *newname, const char *mntopts,
  *      -c     Read pool information from a cachefile instead of searching
  *             devices.
  *
- *       -d    Scan in a specific directory, other than /dev/dsk.  More than
+ *       -d    Scan in a specific directory, other than /dev/.  More than
  *             one directory can be specified using multiple '-d' options.
  *
  *       -D     Scan for previously destroyed pools or import all or only
@@ -1602,6 +1616,11 @@ do_import(nvlist_t *config, const char *newname, const char *mntopts,
  *
  *       -n     See if rewind would work, but don't actually rewind.
  *
+ *       -N     Import the pool but don't mount datasets.
+ *
+ *       -T     Specify a starting txg to use for import. This option is
+ *             intentionally undocumented option for testing purposes.
+ *
  *       -a    Import all pools found.
  *
  *       -o    Set property=value and/or temporary mount options (without '=').
@@ -1620,7 +1639,6 @@ zpool_do_import(int argc, char **argv)
        boolean_t do_all = B_FALSE;
        boolean_t do_destroyed = B_FALSE;
        char *mntopts = NULL;
-       boolean_t do_force = B_FALSE;
        nvpair_t *elem;
        nvlist_t *config;
        uint64_t searchguid = 0;
@@ -1630,17 +1648,18 @@ zpool_do_import(int argc, char **argv)
        nvlist_t *policy = NULL;
        nvlist_t *props = NULL;
        boolean_t first;
-       boolean_t do_verbatim = B_FALSE;
+       int flags = ZFS_IMPORT_NORMAL;
        uint32_t rewind_policy = ZPOOL_NO_REWIND;
        boolean_t dryrun = B_FALSE;
        boolean_t do_rewind = B_FALSE;
        boolean_t xtreme_rewind = B_FALSE;
-       uint64_t pool_state;
+       uint64_t pool_state, txg = -1ULL;
        char *cachefile = NULL;
        importargs_t idata = { 0 };
+       char *endptr;
 
        /* check options */
-       while ((c = getopt(argc, argv, ":aCc:d:DEfFno:rR:VX")) != -1) {
+       while ((c = getopt(argc, argv, ":aCc:d:DEfFmnNo:rR:T:VX")) != -1) {
                switch (c) {
                case 'a':
                        do_all = B_TRUE;
@@ -1665,14 +1684,20 @@ zpool_do_import(int argc, char **argv)
                        do_destroyed = B_TRUE;
                        break;
                case 'f':
-                       do_force = B_TRUE;
+                       flags |= ZFS_IMPORT_ANY_HOST;
                        break;
                case 'F':
                        do_rewind = B_TRUE;
                        break;
+               case 'm':
+                       flags |= ZFS_IMPORT_MISSING_LOG;
+                       break;
                case 'n':
                        dryrun = B_TRUE;
                        break;
+               case 'N':
+                       flags |= ZFS_IMPORT_ONLY;
+                       break;
                case 'o':
                        if ((propval = strchr(optarg, '=')) != NULL) {
                                *propval = '\0';
@@ -1696,8 +1721,18 @@ zpool_do_import(int argc, char **argv)
                            ZPOOL_PROP_CACHEFILE), "none", &props, B_TRUE))
                                goto error;
                        break;
+               case 'T':
+                       errno = 0;
+                       txg = strtoull(optarg, &endptr, 10);
+                       if (errno != 0 || *endptr != '\0') {
+                               (void) fprintf(stderr,
+                                   gettext("invalid txg value\n"));
+                               usage(B_FALSE);
+                       }
+                       rewind_policy = ZPOOL_DO_REWIND | ZPOOL_EXTREME_REWIND;
+                       break;
                case 'V':
-                       do_verbatim = B_TRUE;
+                       flags |= ZFS_IMPORT_VERBATIM;
                        break;
                case 'X':
                        xtreme_rewind = B_TRUE;
@@ -1736,15 +1771,10 @@ zpool_do_import(int argc, char **argv)
 
        /* In the future, we can capture further policy and include it here */
        if (nvlist_alloc(&policy, NV_UNIQUE_NAME, 0) != 0 ||
+           nvlist_add_uint64(policy, ZPOOL_REWIND_REQUEST_TXG, txg) != 0 ||
            nvlist_add_uint32(policy, ZPOOL_REWIND_REQUEST, rewind_policy) != 0)
                goto error;
 
-       if (searchdirs == NULL) {
-               searchdirs = safe_malloc(sizeof (char *));
-               searchdirs[0] = "/dev/dsk";
-               nsearch = 1;
-       }
-
        /* check argument count */
        if (do_all) {
                if (argc != 0) {
@@ -1765,7 +1795,9 @@ zpool_do_import(int argc, char **argv)
                if (argc == 0 && !priv_ineffect(PRIV_SYS_CONFIG)) {
                        (void) fprintf(stderr, gettext("cannot "
                            "discover pools: permission denied\n"));
-                       free(searchdirs);
+                       if (searchdirs != NULL)
+                               free(searchdirs);
+
                        nvlist_free(policy);
                        return (1);
                }
@@ -1833,7 +1865,8 @@ zpool_do_import(int argc, char **argv)
        }
 
        if (err == 1) {
-               free(searchdirs);
+               if (searchdirs != NULL)
+                       free(searchdirs);
                nvlist_free(policy);
                return (1);
        }
@@ -1869,7 +1902,7 @@ zpool_do_import(int argc, char **argv)
 
                        if (do_all) {
                                err |= do_import(config, NULL, mntopts,
-                                   do_force, props, do_verbatim);
+                                   props, flags);
                        } else {
                                show_import(config);
                        }
@@ -1918,7 +1951,7 @@ zpool_do_import(int argc, char **argv)
                        err = B_TRUE;
                } else {
                        err |= do_import(found_config, argc == 1 ? NULL :
-                           argv[1], mntopts, do_force, props, do_verbatim);
+                           argv[1], mntopts, props, flags);
                }
        }
 
@@ -1934,7 +1967,8 @@ error:
        nvlist_free(props);
        nvlist_free(pools);
        nvlist_free(policy);
-       free(searchdirs);
+       if (searchdirs != NULL)
+               free(searchdirs);
 
        return (err ? 1 : 0);
 }
@@ -2176,7 +2210,7 @@ get_interval_count(int *argcp, char **argv, unsigned long *iv,
     unsigned long *cnt)
 {
        unsigned long interval = 0, count = 0;
-       int argc = *argcp, errno;
+       int argc = *argcp;
 
        /*
         * Determine if the last argument is an integer or a pool name
@@ -2324,42 +2358,48 @@ zpool_do_iostat(int argc, char **argv)
                pool_list_update(list);
 
                if ((npools = pool_list_count(list)) == 0)
-                       break;
-
-               /*
-                * Refresh all statistics.  This is done as an explicit step
-                * before calculating the maximum name width, so that any
-                * configuration changes are properly accounted for.
-                */
-               (void) pool_list_iter(list, B_FALSE, refresh_iostat, &cb);
+                       (void) fprintf(stderr, gettext("no pools available\n"));
+               else {
+                       /*
+                        * Refresh all statistics.  This is done as an
+                        * explicit step before calculating the maximum name
+                        * width, so that any * configuration changes are
+                        * properly accounted for.
+                        */
+                       (void) pool_list_iter(list, B_FALSE, refresh_iostat,
+                               &cb);
 
-               /*
-                * Iterate over all pools to determine the maximum width
-                * for the pool / device name column across all pools.
-                */
-               cb.cb_namewidth = 0;
-               (void) pool_list_iter(list, B_FALSE, get_namewidth, &cb);
+                       /*
+                        * Iterate over all pools to determine the maximum width
+                        * for the pool / device name column across all pools.
+                        */
+                       cb.cb_namewidth = 0;
+                       (void) pool_list_iter(list, B_FALSE, get_namewidth,
+                               &cb);
 
-               if (timestamp_fmt != NODATE)
-                       print_timestamp(timestamp_fmt);
+                       if (timestamp_fmt != NODATE)
+                               print_timestamp(timestamp_fmt);
 
-               /*
-                * If it's the first time, or verbose mode, print the header.
-                */
-               if (++cb.cb_iteration == 1 || verbose)
-                       print_iostat_header(&cb);
+                       /*
+                        * If it's the first time, or verbose mode, print the
+                        * header.
+                        */
+                       if (++cb.cb_iteration == 1 || verbose)
+                               print_iostat_header(&cb);
 
-               (void) pool_list_iter(list, B_FALSE, print_iostat, &cb);
+                       (void) pool_list_iter(list, B_FALSE, print_iostat, &cb);
 
-               /*
-                * If there's more than one pool, and we're not in verbose mode
-                * (which prints a separator for us), then print a separator.
-                */
-               if (npools > 1 && !verbose)
-                       print_iostat_separator(&cb);
+                       /*
+                        * If there's more than one pool, and we're not in
+                        * verbose mode (which prints a separator for us),
+                        * then print a separator.
+                        */
+                       if (npools > 1 && !verbose)
+                               print_iostat_separator(&cb);
 
-               if (verbose)
-                       (void) printf("\n");
+                       if (verbose)
+                               (void) printf("\n");
+               }
 
                /*
                 * Flush the output so that redirection to a file isn't buffered
@@ -2412,9 +2452,9 @@ print_header(zprop_list_t *pl)
                if (pl->pl_next == NULL && !right_justify)
                        (void) printf("%s", header);
                else if (right_justify)
-                       (void) printf("%*s", pl->pl_width, header);
+                       (void) printf("%*s", (int)pl->pl_width, header);
                else
-                       (void) printf("%-*s", pl->pl_width, header);
+                       (void) printf("%-*s", (int)pl->pl_width, header);
        }
 
        (void) printf("\n");
@@ -2558,10 +2598,12 @@ zpool_do_list(int argc, char **argv)
                ret = for_each_pool(argc, argv, B_TRUE, &cb.cb_proplist,
                    list_callback, &cb);
 
-               if (argc == 0 && cb.cb_first && !cb.cb_scripted) {
-                       (void) printf(gettext("no pools available\n"));
+               if (argc == 0 && cb.cb_first)
+                       (void) fprintf(stderr, gettext("no pools available\n"));
+               else if (argc && cb.cb_first) {
+                       /* cannot open the given pool */
                        zprop_free_list(cb.cb_proplist);
-                       return (0);
+                       return (1);
                }
 
                if (interval == 0)
@@ -2577,33 +2619,6 @@ zpool_do_list(int argc, char **argv)
        return (ret);
 }
 
-static nvlist_t *
-zpool_get_vdev_by_name(nvlist_t *nv, char *name)
-{
-       nvlist_t **child;
-       uint_t c, children;
-       nvlist_t *match;
-       char *path;
-
-       if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
-           &child, &children) != 0) {
-               verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path) == 0);
-               if (strncmp(name, "/dev/dsk/", 9) == 0)
-                       name += 9;
-               if (strncmp(path, "/dev/dsk/", 9) == 0)
-                       path += 9;
-               if (strcmp(name, path) == 0)
-                       return (nv);
-               return (NULL);
-       }
-
-       for (c = 0; c < children; c++)
-               if ((match = zpool_get_vdev_by_name(child[c], name)) != NULL)
-                       return (match);
-
-       return (NULL);
-}
-
 static int
 zpool_do_attach_or_replace(int argc, char **argv, int replacing)
 {
@@ -2676,7 +2691,7 @@ zpool_do_attach_or_replace(int argc, char **argv, int replacing)
                return (1);
        }
 
-       nvroot = make_root_vdev(zhp, force, B_FALSE, replacing, B_FALSE,
+       nvroot = make_root_vdev(zhp, NULL, force, B_FALSE, replacing, B_FALSE,
            argc, argv);
        if (nvroot == NULL) {
                zpool_close(zhp);
@@ -3217,7 +3232,7 @@ void
 print_scan_status(pool_scan_stat_t *ps)
 {
        time_t start, end;
-       uint64_t elapsed, mins_left;
+       uint64_t elapsed, mins_left, hours_left;
        uint64_t pass_exam, examined, total;
        uint_t rate;
        double fraction_done;
@@ -3243,7 +3258,7 @@ print_scan_status(pool_scan_stat_t *ps)
         */
        if (ps->pss_state == DSS_FINISHED) {
                uint64_t minutes_taken = (end - start) / 60;
-               char *fmt;
+               char *fmt = NULL;
 
                if (ps->pss_func == POOL_SCAN_SCRUB) {
                        fmt = gettext("scrub repaired %s in %lluh%um with "
@@ -3294,15 +3309,24 @@ print_scan_status(pool_scan_stat_t *ps)
        rate = pass_exam / elapsed;
        rate = rate ? rate : 1;
        mins_left = ((total - examined) / rate) / 60;
+       hours_left = mins_left / 60;
 
        zfs_nicenum(examined, examined_buf, sizeof (examined_buf));
        zfs_nicenum(total, total_buf, sizeof (total_buf));
        zfs_nicenum(rate, rate_buf, sizeof (rate_buf));
 
-       (void) printf(gettext("    %s scanned out of %s at "
-           "%s/s, %lluh%um to go\n"), examined_buf, total_buf, rate_buf,
-           (u_longlong_t)(mins_left / 60),
-           (uint_t)(mins_left % 60));
+       /*
+        * do not print estimated time if hours_left is more than 30 days
+        */
+       (void) printf(gettext("    %s scanned out of %s at %s/s"),
+           examined_buf, total_buf, rate_buf);
+       if (hours_left < (30 * 24)) {
+               (void) printf(gettext(", %lluh%um to go\n"),
+                   (u_longlong_t)hours_left, (uint_t)(mins_left % 60));
+       } else {
+               (void) printf(gettext(
+                   ", (scan is slow, no estimated time)\n"));
+       }
 
        if (ps->pss_func == POOL_SCAN_RESILVER) {
                (void) printf(gettext("    %s resilvered, %.2f%% done\n"),
@@ -3424,7 +3448,7 @@ print_dedup_stats(nvlist_t *config)
  *        pool: tank
  *     status: DEGRADED
  *     reason: One or more devices ...
- *         see: http://www.sun.com/msg/ZFS-xxxx-01
+ *         see: http://zfsonlinux.org/msg/ZFS-xxxx-01
  *     config:
  *             mirror          DEGRADED
  *                c1t0d0       OK
@@ -3632,7 +3656,7 @@ status_callback(zpool_handle_t *zhp, void *data)
        }
 
        if (msgid != NULL)
-               (void) printf(gettext("   see: http://www.sun.com/msg/%s\n"),
+               (void) printf(gettext("   see: http://zfsonlinux.org/msg/%s\n"),
                    msgid);
 
        if (config != NULL) {
@@ -3769,7 +3793,7 @@ zpool_do_status(int argc, char **argv)
                    status_callback, &cb);
 
                if (argc == 0 && cb.cb_count == 0)
-                       (void) printf(gettext("no pools available\n"));
+                       (void) fprintf(stderr, gettext("no pools available\n"));
                else if (cb.cb_explain && cb.cb_first && cb.cb_allpools)
                        (void) printf(gettext("all pools are healthy\n"));
 
@@ -3871,7 +3895,7 @@ upgrade_one(zpool_handle_t *zhp, void *data)
        if (cur_version > cbp->cb_version) {
                (void) printf(gettext("Pool '%s' is already formatted "
                    "using more current version '%llu'.\n"),
-                   zpool_get_name(zhp), cur_version);
+                   zpool_get_name(zhp), (u_longlong_t) cur_version);
                return (0);
        }
        if (cur_version == cbp->cb_version) {
@@ -4009,6 +4033,9 @@ zpool_do_upgrade(int argc, char **argv)
                (void) printf(gettext(" 25  Improved scrub stats\n"));
                (void) printf(gettext(" 26  Improved snapshot deletion "
                    "performance\n"));
+               (void) printf(gettext(" 27  Improved snapshot creation "
+                   "performance\n"));
+               (void) printf(gettext(" 28  Multiple vdev replacements\n"));
                (void) printf(gettext("\nFor more information on a particular "
                    "version, including supported releases,\n"));
                (void) printf(gettext("see the ZFS Administration Guide.\n\n"));
@@ -4109,8 +4136,8 @@ get_history_one(zpool_handle_t *zhp, void *data)
                                continue;
                        (void) snprintf(internalstr,
                            sizeof (internalstr),
-                           "[internal %s txg:%lld] %s",
-                           zfs_history_event_names[ievent], txg,
+                           "[internal %s txg:%llu] %s",
+                           zfs_history_event_names[ievent], (u_longlong_t)txg,
                            pathstr);
                        cmdstr = internalstr;
                }
@@ -4192,13 +4219,343 @@ zpool_do_history(int argc, char **argv)
            &cbdata);
 
        if (argc == 0 && cbdata.first == B_TRUE) {
-               (void) printf(gettext("no pools available\n"));
+               (void) fprintf(stderr, gettext("no pools available\n"));
                return (0);
        }
 
        return (ret);
 }
 
+typedef struct ev_opts {
+       int verbose;
+       int scripted;
+       int follow;
+       int clear;
+} ev_opts_t;
+
+static void
+zpool_do_events_short(nvlist_t *nvl)
+{
+       char ctime_str[26], str[32], *ptr;
+       int64_t *tv;
+       uint_t n;
+
+       verify(nvlist_lookup_int64_array(nvl, FM_EREPORT_TIME, &tv, &n) == 0);
+       memset(str, ' ', 32);
+       (void) ctime_r((const time_t *)&tv[0], ctime_str);
+       (void) strncpy(str,    ctime_str+4,  6);             /* 'Jun 30'     */
+       (void) strncpy(str+7,  ctime_str+20, 4);             /* '1993'       */
+       (void) strncpy(str+12, ctime_str+11, 8);             /* '21:49:08'   */
+       (void) sprintf(str+20, ".%09lld", (longlong_t)tv[1]);/* '.123456789' */
+       (void) printf(gettext("%s "), str);
+
+       verify(nvlist_lookup_string(nvl, FM_CLASS, &ptr) == 0);
+       (void) printf(gettext("%s\n"), ptr);
+}
+
+static void
+zpool_do_events_nvprint(nvlist_t *nvl, int depth)
+{
+       nvpair_t *nvp;
+
+       for (nvp = nvlist_next_nvpair(nvl, NULL);
+           nvp != NULL; nvp = nvlist_next_nvpair(nvl, nvp)) {
+
+               data_type_t type = nvpair_type(nvp);
+               const char *name = nvpair_name(nvp);
+
+               boolean_t b;
+               uint8_t i8;
+               uint16_t i16;
+               uint32_t i32;
+               uint64_t i64;
+               char *str;
+               nvlist_t *cnv;
+
+               printf(gettext("%*s%s = "), depth, "", name);
+
+               switch (type) {
+               case DATA_TYPE_BOOLEAN:
+                       printf(gettext("%s"), "1");
+                       break;
+
+               case DATA_TYPE_BOOLEAN_VALUE:
+                       (void) nvpair_value_boolean_value(nvp, &b);
+                       printf(gettext("%s"), b ? "1" : "0");
+                       break;
+
+               case DATA_TYPE_BYTE:
+                       (void) nvpair_value_byte(nvp, &i8);
+                       printf(gettext("0x%x"), i8);
+                       break;
+
+               case DATA_TYPE_INT8:
+                       (void) nvpair_value_int8(nvp, (void *)&i8);
+                       printf(gettext("0x%x"), i8);
+                       break;
+
+               case DATA_TYPE_UINT8:
+                       (void) nvpair_value_uint8(nvp, &i8);
+                       printf(gettext("0x%x"), i8);
+                       break;
+
+               case DATA_TYPE_INT16:
+                       (void) nvpair_value_int16(nvp, (void *)&i16);
+                       printf(gettext("0x%x"), i16);
+                       break;
+
+               case DATA_TYPE_UINT16:
+                       (void) nvpair_value_uint16(nvp, &i16);
+                       printf(gettext("0x%x"), i16);
+                       break;
+
+               case DATA_TYPE_INT32:
+                       (void) nvpair_value_int32(nvp, (void *)&i32);
+                       printf(gettext("0x%x"), i32);
+                       break;
+
+               case DATA_TYPE_UINT32:
+                       (void) nvpair_value_uint32(nvp, &i32);
+                       printf(gettext("0x%x"), i32);
+                       break;
+
+               case DATA_TYPE_INT64:
+                       (void) nvpair_value_int64(nvp, (void *)&i64);
+                       printf(gettext("0x%llx"), (u_longlong_t)i64);
+                       break;
+
+               case DATA_TYPE_UINT64:
+                       (void) nvpair_value_uint64(nvp, &i64);
+                       printf(gettext("0x%llx"), (u_longlong_t)i64);
+                       break;
+
+               case DATA_TYPE_HRTIME:
+                       (void) nvpair_value_hrtime(nvp, (void *)&i64);
+                       printf(gettext("0x%llx"), (u_longlong_t)i64);
+                       break;
+
+               case DATA_TYPE_STRING:
+                       (void) nvpair_value_string(nvp, &str);
+                       printf(gettext("\"%s\""), str ? str : "<NULL>");
+                       break;
+
+               case DATA_TYPE_NVLIST:
+                       printf(gettext("(embedded nvlist)\n"));
+                       (void) nvpair_value_nvlist(nvp, &cnv);
+                       zpool_do_events_nvprint(cnv, depth + 8);
+                       printf(gettext("%*s(end %s)"), depth, "", name);
+                       break;
+
+               case DATA_TYPE_NVLIST_ARRAY: {
+                       nvlist_t **val;
+                       uint_t i, nelem;
+
+                       (void) nvpair_value_nvlist_array(nvp, &val, &nelem);
+                       printf(gettext("(%d embedded nvlists)\n"), nelem);
+                       for (i = 0; i < nelem; i++) {
+                               printf(gettext("%*s%s[%d] = %s\n"),
+                                      depth, "", name, i, "(embedded nvlist)");
+                               zpool_do_events_nvprint(val[i], depth + 8);
+                               printf(gettext("%*s(end %s[%i])\n"),
+                                      depth, "", name, i);
+                       }
+                       printf(gettext("%*s(end %s)\n"), depth, "", name);
+                       }
+                       break;
+
+               case DATA_TYPE_INT8_ARRAY: {
+                       int8_t *val;
+                       uint_t i, nelem;
+
+                       (void) nvpair_value_int8_array(nvp, &val, &nelem);
+                       for (i = 0; i < nelem; i++)
+                               printf(gettext("0x%x "), val[i]);
+
+                       break;
+                       }
+
+               case DATA_TYPE_UINT8_ARRAY: {
+                       uint8_t *val;
+                       uint_t i, nelem;
+
+                       (void) nvpair_value_uint8_array(nvp, &val, &nelem);
+                       for (i = 0; i < nelem; i++)
+                               printf(gettext("0x%x "), val[i]);
+
+                       break;
+                       }
+
+               case DATA_TYPE_INT16_ARRAY: {
+                       int16_t *val;
+                       uint_t i, nelem;
+
+                       (void) nvpair_value_int16_array(nvp, &val, &nelem);
+                       for (i = 0; i < nelem; i++)
+                               printf(gettext("0x%x "), val[i]);
+
+                       break;
+                       }
+
+               case DATA_TYPE_UINT16_ARRAY: {
+                       uint16_t *val;
+                       uint_t i, nelem;
+
+                       (void) nvpair_value_uint16_array(nvp, &val, &nelem);
+                       for (i = 0; i < nelem; i++)
+                               printf(gettext("0x%x "), val[i]);
+
+                       break;
+                       }
+
+               case DATA_TYPE_INT32_ARRAY: {
+                       int32_t *val;
+                       uint_t i, nelem;
+
+                       (void) nvpair_value_int32_array(nvp, &val, &nelem);
+                       for (i = 0; i < nelem; i++)
+                               printf(gettext("0x%x "), val[i]);
+
+                       break;
+                       }
+
+               case DATA_TYPE_UINT32_ARRAY: {
+                       uint32_t *val;
+                       uint_t i, nelem;
+
+                       (void) nvpair_value_uint32_array(nvp, &val, &nelem);
+                       for (i = 0; i < nelem; i++)
+                               printf(gettext("0x%x "), val[i]);
+
+                       break;
+                       }
+
+               case DATA_TYPE_INT64_ARRAY: {
+                       int64_t *val;
+                       uint_t i, nelem;
+
+                       (void) nvpair_value_int64_array(nvp, &val, &nelem);
+                       for (i = 0; i < nelem; i++)
+                               printf(gettext("0x%llx "), (u_longlong_t)val[i]);
+
+                       break;
+                       }
+
+               case DATA_TYPE_UINT64_ARRAY: {
+                       uint64_t *val;
+                       uint_t i, nelem;
+
+                       (void) nvpair_value_uint64_array(nvp, &val, &nelem);
+                       for (i = 0; i < nelem; i++)
+                               printf(gettext("0x%llx "), (u_longlong_t)val[i]);
+
+                       break;
+                       }
+
+               case DATA_TYPE_STRING_ARRAY:
+               case DATA_TYPE_BOOLEAN_ARRAY:
+               case DATA_TYPE_BYTE_ARRAY:
+               case DATA_TYPE_DOUBLE:
+               case DATA_TYPE_UNKNOWN:
+                       printf(gettext("<unknown>"));
+                       break;
+               }
+
+               printf(gettext("\n"));
+       }
+}
+
+static int
+zpool_do_events_next(ev_opts_t *opts)
+{
+       nvlist_t *nvl;
+       int cleanup_fd, ret, dropped;
+
+        cleanup_fd = open(ZFS_DEV, O_RDWR);
+        VERIFY(cleanup_fd >= 0);
+
+       if (!opts->scripted)
+               (void) printf(gettext("%-30s %s\n"), "TIME", "CLASS");
+
+       while (1) {
+               ret = zpool_events_next(g_zfs, &nvl, &dropped,
+                   !!opts->follow, cleanup_fd);
+               if (ret || nvl == NULL)
+                       break;
+
+               if (dropped > 0)
+                       (void) printf(gettext("dropped %d events\n"), dropped);
+
+               zpool_do_events_short(nvl);
+
+               if (opts->verbose) {
+                       zpool_do_events_nvprint(nvl, 8);
+                       printf(gettext("\n"));
+               }
+
+               nvlist_free(nvl);
+       }
+
+        VERIFY(0 == close(cleanup_fd));
+
+       return (ret);
+}
+
+static int
+zpool_do_events_clear(ev_opts_t *opts)
+{
+       int count, ret;
+
+       ret = zpool_events_clear(g_zfs, &count);
+       if (!ret)
+               (void) printf(gettext("cleared %d events\n"), count);
+
+       return (ret);
+}
+
+/*
+ * zpool events [-vfc]
+ *
+ * Displays events logs by ZFS.
+ */
+int
+zpool_do_events(int argc, char **argv)
+{
+       ev_opts_t opts = { 0 };
+       int ret;
+       int c;
+
+       /* check options */
+       while ((c = getopt(argc, argv, "vHfc")) != -1) {
+               switch (c) {
+               case 'v':
+                       opts.verbose = 1;
+                       break;
+               case 'H':
+                       opts.scripted = 1;
+                       break;
+               case 'f':
+                       opts.follow = 1;
+                       break;
+               case 'c':
+                       opts.clear = 1;
+                       break;
+               case '?':
+                       (void) fprintf(stderr, gettext("invalid option '%c'\n"),
+                           optopt);
+                       usage(B_FALSE);
+               }
+       }
+       argc -= optind;
+       argv += optind;
+
+       if (opts.clear)
+               ret = zpool_do_events_clear(&opts);
+       else
+               ret = zpool_do_events_next(&opts);
+
+       return ret;
+}
+
 static int
 get_callback(zpool_handle_t *zhp, void *data)
 {
@@ -4354,20 +4711,12 @@ int
 main(int argc, char **argv)
 {
        int ret;
-       int i;
+       int i = 0;
        char *cmdname;
 
        (void) setlocale(LC_ALL, "");
        (void) textdomain(TEXT_DOMAIN);
 
-       if ((g_zfs = libzfs_init()) == NULL) {
-               (void) fprintf(stderr, gettext("internal error: failed to "
-                   "initialize ZFS library\n"));
-               return (1);
-       }
-
-       libzfs_print_on_error(g_zfs, B_TRUE);
-
        opterr = 0;
 
        /*
@@ -4383,9 +4732,15 @@ main(int argc, char **argv)
        /*
         * Special case '-?'
         */
-       if (strcmp(cmdname, "-?") == 0)
+       if ((strcmp(cmdname, "-?") == 0) ||
+            strcmp(cmdname, "--help") == 0)
                usage(B_TRUE);
 
+       if ((g_zfs = libzfs_init()) == NULL)
+               return (1);
+
+       libzfs_print_on_error(g_zfs, B_TRUE);
+
        zpool_set_history_str("zpool", argc, argv, history_str);
        verify(zpool_stage_history(g_zfs, history_str) == 0);
 
@@ -4412,6 +4767,7 @@ main(int argc, char **argv)
                (void) fprintf(stderr, gettext("unrecognized "
                    "command '%s'\n"), cmdname);
                usage(B_FALSE);
+               ret = 1;
        }
 
        libzfs_fini(g_zfs);