diff options
| author | sjelinek <none@none> | 2006-07-17 12:08:55 -0700 |
|---|---|---|
| committer | sjelinek <none@none> | 2006-07-17 12:08:55 -0700 |
| commit | b6825278356f1c9ddb8765d2f2b1d4fb62be1dbd (patch) | |
| tree | cf84125730e79e06c4a0a619f513c23100f7576a /usr | |
| parent | d7bdf1f4ea70987e7c25c280bde23207f6446fdd (diff) | |
| download | illumos-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')
| -rw-r--r-- | usr/src/cmd/zfs/zfs_iter.c | 120 | ||||
| -rw-r--r-- | usr/src/cmd/zfs/zfs_iter.h | 12 | ||||
| -rw-r--r-- | usr/src/cmd/zfs/zfs_main.c | 43 |
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)); } /* |
