summaryrefslogtreecommitdiff
path: root/usr/src/cmd/zfs
diff options
context:
space:
mode:
authorsjelinek <none@none>2006-07-17 12:08:55 -0700
committersjelinek <none@none>2006-07-17 12:08:55 -0700
commitb6825278356f1c9ddb8765d2f2b1d4fb62be1dbd (patch)
treecf84125730e79e06c4a0a619f513c23100f7576a /usr/src/cmd/zfs
parentd7bdf1f4ea70987e7c25c280bde23207f6446fdd (diff)
downloadillumos-joyent-b6825278356f1c9ddb8765d2f2b1d4fb62be1dbd.tar.gz
PSARC 2006/308 zfs list sort option
6276925 option to sort 'zfs list' output Contributed by Rich Lowe <richlowe@richlowe.net>
Diffstat (limited to 'usr/src/cmd/zfs')
-rw-r--r--usr/src/cmd/zfs/zfs_iter.c120
-rw-r--r--usr/src/cmd/zfs/zfs_iter.h12
-rw-r--r--usr/src/cmd/zfs/zfs_main.c43
3 files changed, 163 insertions, 12 deletions
diff --git a/usr/src/cmd/zfs/zfs_iter.c b/usr/src/cmd/zfs/zfs_iter.c
index 9f8f37b765..814a165393 100644
--- a/usr/src/cmd/zfs/zfs_iter.c
+++ b/usr/src/cmd/zfs/zfs_iter.c
@@ -35,14 +35,14 @@
#include <libzfs.h>
#include "zfs_util.h"
+#include "zfs_iter.h"
/*
* This is a private interface used to gather up all the datasets specified on
* the command line so that we can iterate over them in order.
*
* First, we iterate over all filesystems, gathering them together into an
- * AVL tree sorted by name. For snapshots, we order them according to
- * creation time. We report errors for any explicitly specified datasets
+ * AVL tree. We report errors for any explicitly specified datasets
* that we couldn't open.
*
* When finished, we have an AVL tree of ZFS handles. We go through and execute
@@ -58,6 +58,7 @@ typedef struct callback_data {
uu_avl_t *cb_avl;
int cb_recurse;
zfs_type_t cb_types;
+ zfs_sort_column_t *cb_sortcol;
} callback_data_t;
uu_avl_pool_t *avl_pool;
@@ -81,7 +82,8 @@ zfs_callback(zfs_handle_t *zhp, void *data)
node->zn_handle = zhp;
uu_avl_node_init(node, &node->zn_avlnode, avl_pool);
- if (uu_avl_find(cb->cb_avl, node, NULL, &idx) == NULL) {
+ if (uu_avl_find(cb->cb_avl, node, cb->cb_sortcol,
+ &idx) == NULL) {
uu_avl_insert(cb->cb_avl, node, idx);
dontclose = 1;
} else {
@@ -103,6 +105,39 @@ zfs_callback(zfs_handle_t *zhp, void *data)
return (0);
}
+void
+zfs_add_sort_column(zfs_sort_column_t **sc, zfs_prop_t prop,
+ boolean_t reverse)
+{
+ zfs_sort_column_t *col;
+
+ col = safe_malloc(sizeof (zfs_sort_column_t));
+
+ col->sc_prop = prop;
+ col->sc_reverse = reverse;
+ col->sc_next = NULL;
+
+ if (*sc == NULL) {
+ col->sc_last = col;
+ *sc = col;
+ } else {
+ (*sc)->sc_last->sc_next = col;
+ (*sc)->sc_last = col;
+ }
+}
+
+void
+zfs_free_sort_columns(zfs_sort_column_t *sc)
+{
+ zfs_sort_column_t *col;
+
+ while (sc != NULL) {
+ col = sc->sc_next;
+ free(sc);
+ sc = col;
+ }
+}
+
/* ARGSUSED */
static int
zfs_compare(const void *larg, const void *rarg, void *unused)
@@ -158,9 +193,83 @@ zfs_compare(const void *larg, const void *rarg, void *unused)
return (ret);
}
+/*
+ * Sort datasets by specified columns.
+ *
+ * o Numeric types sort in ascending order.
+ * o String types sort in alphabetical order.
+ * o Types inappropriate for a row sort that row to the literal
+ * bottom, regardless of the specified ordering.
+ *
+ * If no sort columns are specified, or two datasets compare equally
+ * across all specified columns, they are sorted alphabetically by name
+ * with snapshots grouped under their parents.
+ */
+static int
+zfs_sort(const void *larg, const void *rarg, void *data)
+{
+ zfs_handle_t *l = ((zfs_node_t *)larg)->zn_handle;
+ zfs_handle_t *r = ((zfs_node_t *)rarg)->zn_handle;
+ zfs_sort_column_t *sc = (zfs_sort_column_t *)data;
+ zfs_sort_column_t *psc;
+
+ for (psc = sc; psc != NULL; psc = psc->sc_next) {
+ char lstr[ZFS_MAXPROPLEN], rstr[ZFS_MAXPROPLEN];
+ uint64_t lnum, rnum;
+ int lvalid, rvalid;
+ int ret = 0;
+
+ if (zfs_prop_is_string(psc->sc_prop)) {
+ lvalid = zfs_prop_get(l, psc->sc_prop, lstr,
+ sizeof (lstr), NULL, NULL, 0, B_TRUE);
+ rvalid = zfs_prop_get(r, psc->sc_prop, rstr,
+ sizeof (rstr), NULL, NULL, 0, B_TRUE);
+
+ if ((lvalid == -1) && (rvalid == -1))
+ continue;
+ if (lvalid == -1)
+ return (1);
+ else if (rvalid == -1)
+ return (-1);
+
+ ret = strcmp(lstr, rstr);
+ } else {
+ lvalid = zfs_prop_valid_for_type(psc->sc_prop,
+ zfs_get_type(l));
+ rvalid = zfs_prop_valid_for_type(psc->sc_prop,
+ zfs_get_type(r));
+
+ if (!lvalid && !rvalid)
+ continue;
+ else if (!lvalid)
+ return (1);
+ else if (!rvalid)
+ return (-1);
+
+ (void) zfs_prop_get_numeric(l, psc->sc_prop, &lnum,
+ NULL, NULL, 0);
+ (void) zfs_prop_get_numeric(r, psc->sc_prop, &rnum,
+ NULL, NULL, 0);
+
+ if (lnum < rnum)
+ ret = -1;
+ else if (lnum > rnum)
+ ret = 1;
+ }
+
+ if (ret != 0) {
+ if (psc->sc_reverse == B_TRUE)
+ ret = (ret < 0) ? 1 : -1;
+ return (ret);
+ }
+ }
+
+ return (zfs_compare(larg, rarg, NULL));
+}
+
int
zfs_for_each(int argc, char **argv, boolean_t recurse, zfs_type_t types,
- zfs_iter_f callback, void *data)
+ zfs_sort_column_t *sortcol, zfs_iter_f callback, void *data)
{
callback_data_t cb;
int ret = 0;
@@ -168,7 +277,7 @@ zfs_for_each(int argc, char **argv, boolean_t recurse, zfs_type_t types,
uu_avl_walk_t *walk;
avl_pool = uu_avl_pool_create("zfs_pool", sizeof (zfs_node_t),
- offsetof(zfs_node_t, zn_avlnode), zfs_compare, UU_DEFAULT);
+ offsetof(zfs_node_t, zn_avlnode), zfs_sort, UU_DEFAULT);
if (avl_pool == NULL) {
(void) fprintf(stderr,
@@ -176,6 +285,7 @@ zfs_for_each(int argc, char **argv, boolean_t recurse, zfs_type_t types,
exit(1);
}
+ cb.cb_sortcol = sortcol;
cb.cb_recurse = recurse;
cb.cb_types = types;
if ((cb.cb_avl = uu_avl_create(avl_pool, NULL, UU_DEFAULT)) == NULL) {
diff --git a/usr/src/cmd/zfs/zfs_iter.h b/usr/src/cmd/zfs/zfs_iter.h
index c69049b28f..de734e8d4e 100644
--- a/usr/src/cmd/zfs/zfs_iter.h
+++ b/usr/src/cmd/zfs/zfs_iter.h
@@ -32,7 +32,17 @@
extern "C" {
#endif
-int zfs_for_each(int, char **, boolean_t, zfs_type_t, zfs_iter_f, void *);
+typedef struct zfs_sort_column {
+ struct zfs_sort_column *sc_next;
+ struct zfs_sort_column *sc_last;
+ zfs_prop_t sc_prop;
+ boolean_t sc_reverse;
+} zfs_sort_column_t;
+
+int zfs_for_each(int, char **, boolean_t, zfs_type_t, zfs_sort_column_t *,
+ zfs_iter_f, void *);
+void zfs_add_sort_column(zfs_sort_column_t **, zfs_prop_t, boolean_t);
+void zfs_free_sort_columns(zfs_sort_column_t *);
#ifdef __cplusplus
}
diff --git a/usr/src/cmd/zfs/zfs_main.c b/usr/src/cmd/zfs/zfs_main.c
index ecffffe330..a8b734a933 100644
--- a/usr/src/cmd/zfs/zfs_main.c
+++ b/usr/src/cmd/zfs/zfs_main.c
@@ -177,6 +177,8 @@ get_usage(zfs_help_t idx)
case HELP_LIST:
return (gettext("\tlist [-rH] [-o property[,property]...] "
"[-t type[,type]...]\n"
+ "\t [-s property [-s property]...]"
+ " [-S property [-S property]...]\n"
"\t [filesystem|volume|snapshot] ...\n"));
case HELP_MOUNT:
return (gettext("\tmount\n"
@@ -1051,8 +1053,9 @@ zfs_do_get(int argc, char **argv)
}
/* run for each object */
- return (zfs_for_each(argc, argv, recurse, ZFS_TYPE_ANY,
+ return (zfs_for_each(argc, argv, recurse, ZFS_TYPE_ANY, NULL,
get_callback, &cb));
+
}
/*
@@ -1135,17 +1138,21 @@ zfs_do_inherit(int argc, char **argv)
}
return (zfs_for_each(argc - 1, argv + 1, recurse,
- ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME,
+ ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, NULL,
inherit_callback, (void *)prop));
}
/*
- * list [-rH] [-o property[,property]...] [-t type[,type]...] <dataset> ...
+ * list [-rH] [-o property[,property]...] [-t type[,type]...]
+ * [-s property [-s property]...] [-S property [-S property]...]
+ * <dataset> ...
*
* -r Recurse over all children
* -H Scripted mode; elide headers and separate colums by tabs
* -o Control which fields to display.
* -t Control which object types to display.
+ * -s Specify sort columns, descending order.
+ * -S Specify sort columns, ascending order.
*
* When given no arguments, lists all filesystems in the system.
* Otherwise, list the specified datasets, optionally recursing down them if
@@ -1254,9 +1261,12 @@ zfs_do_list(int argc, char **argv)
char *type_subopts[] = { "filesystem", "volume", "snapshot", NULL };
char *badopt;
int alloffset;
+ zfs_sort_column_t *sortcol = NULL;
/* check options */
- while ((c = getopt(argc, argv, ":o:rt:H")) != -1) {
+ while ((c = getopt(argc, argv, ":o:rt:Hs:S:")) != -1) {
+ zfs_prop_t prop;
+
switch (c) {
case 'o':
fields = optarg;
@@ -1267,6 +1277,24 @@ zfs_do_list(int argc, char **argv)
case 'H':
scripted = B_TRUE;
break;
+ case 's':
+ if ((prop = zfs_name_to_prop(optarg)) ==
+ ZFS_PROP_INVAL) {
+ (void) fprintf(stderr,
+ gettext("invalid property '%s'\n"), optarg);
+ usage(B_FALSE);
+ }
+ zfs_add_sort_column(&sortcol, prop, B_FALSE);
+ break;
+ case 'S':
+ if ((prop = zfs_name_to_prop(optarg)) ==
+ ZFS_PROP_INVAL) {
+ (void) fprintf(stderr,
+ gettext("invalid property '%s'\n"), optarg);
+ usage(B_FALSE);
+ }
+ zfs_add_sort_column(&sortcol, prop, B_TRUE);
+ break;
case 't':
types = 0;
while (*optarg != '\0') {
@@ -1334,7 +1362,10 @@ zfs_do_list(int argc, char **argv)
cb.cb_scripted = scripted;
cb.cb_first = B_TRUE;
- ret = zfs_for_each(argc, argv, recurse, types, list_callback, &cb);
+ ret = zfs_for_each(argc, argv, recurse, types, sortcol,
+ list_callback, &cb);
+
+ zfs_free_sort_columns(sortcol);
if (ret == 0 && cb.cb_first)
(void) printf(gettext("no datasets available\n"));
@@ -1777,7 +1808,7 @@ zfs_do_set(int argc, char **argv)
return (1);
return (zfs_for_each(argc - 2, argv + 2, B_FALSE,
- ZFS_TYPE_ANY, set_callback, &cb));
+ ZFS_TYPE_ANY, NULL, set_callback, &cb));
}
/*