bectl(8): implement sorting for 'bectl list' output
Allow 'bectl list' to sort output by a given property name. The property name is passed in using a command-line flag, '-c' for ascending order and '-C' for descending order. The properties allowed to sort by are: - name (the default output, even if '-c' or '-C' are not used) - creation - origin - used - usedds - usedsnap - usedrefreserv The default output for 'bectl list' is now ascending alphabetical order of BE name. To sort by creation time from earliest to latest, the command would be 'bectl list -c creation' Submitted by: Rob Fairbanks <rob.fx907 gmail com> Reviewed by: ler MFC after: 1 week Differential Revision: https://reviews.freebsd.org/D20818
This commit is contained in:
parent
ceb1b924ec
commit
a7bf7ad218
@ -18,7 +18,7 @@
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd May 12, 2019
|
||||
.Dd September 4, 2019
|
||||
.Dt BECTL 8
|
||||
.Os
|
||||
.Sh NAME
|
||||
@ -57,6 +57,9 @@
|
||||
.Nm
|
||||
.Cm list
|
||||
.Op Fl aDHs
|
||||
.Op Fl c Ar property
|
||||
.Op Fl C Ar property
|
||||
.Oo Bro Fl c Ar property | Fl C Ar property Brc Oc
|
||||
.Nm
|
||||
.Cm mount
|
||||
.Ar beName
|
||||
@ -234,7 +237,12 @@ generated by
|
||||
.El
|
||||
.Pp
|
||||
All default parameters may be overwritten.
|
||||
.It Cm list Op Fl aDHs
|
||||
.It Xo
|
||||
.Cm list
|
||||
.Op Fl DHas
|
||||
.Oo Bro Fl c Ar property | Fl C Ar property Brc Oc
|
||||
.Xc
|
||||
.Pp
|
||||
Display all boot environments.
|
||||
The
|
||||
.Em Active
|
||||
@ -245,21 +253,44 @@ active on reboot
|
||||
or both
|
||||
.Pq Em \&NR .
|
||||
.Pp
|
||||
If
|
||||
.Fl a
|
||||
is used, display all datasets.
|
||||
If
|
||||
.Fl D
|
||||
is used, display the full space usage for each boot environment, assuming all
|
||||
.Bl -tag -width indent
|
||||
.It Fl a
|
||||
Display all datasets.
|
||||
.It Fl D
|
||||
Display the full space usage for each boot environment, assuming all
|
||||
other boot environments were destroyed.
|
||||
The
|
||||
.Fl H
|
||||
option is used for scripting.
|
||||
It does not print headers and separate fields by a single tab instead of
|
||||
.It Fl H
|
||||
Used for scripting.
|
||||
Do not print headers and separate fields by a single tab instead of
|
||||
arbitrary white space.
|
||||
If
|
||||
.It Fl s
|
||||
Display all snapshots as well.
|
||||
.It Fl c Ar property
|
||||
Sort boot environments by given property name.
|
||||
The following properties are supported:
|
||||
.Pp
|
||||
.Bl -tag -width 4n -offset indent -compact
|
||||
.It name (default output)
|
||||
.It creation
|
||||
.It origin
|
||||
.It used
|
||||
.It usedds
|
||||
.It usedsnap
|
||||
.It usedrefreserv
|
||||
.El
|
||||
.It Fl C Ar property
|
||||
Same as the
|
||||
.Fl c
|
||||
option, but displays in descending order.
|
||||
.El
|
||||
.Pp
|
||||
The
|
||||
.Fl D
|
||||
option is ignored when either the
|
||||
.Fl s
|
||||
is used, display all snapshots as well.
|
||||
or
|
||||
.Fl a
|
||||
option is used.
|
||||
.It Cm mount Ar beName Op Ar mountpoint
|
||||
Temporarily mount the boot environment.
|
||||
Mount at the specified
|
||||
|
@ -80,7 +80,7 @@ usage(bool explicit)
|
||||
"\tbectl jail {-b | -U} [{-o key=value | -u key}]... "
|
||||
"{jailID | jailName}\n"
|
||||
"\t bootenv [utility [argument ...]]\n"
|
||||
"\tbectl list [-DHas]\n"
|
||||
"\tbectl list [-DHas] [{-c property | -C property}]\n"
|
||||
"\tbectl mount beName [mountpoint]\n"
|
||||
"\tbectl rename origBeName newBeName\n"
|
||||
"\tbectl {ujail | unjail} {jailID | jailName} bootenv\n"
|
||||
|
@ -38,6 +38,12 @@ __FBSDID("$FreeBSD$");
|
||||
|
||||
#include "bectl.h"
|
||||
|
||||
struct sort_column {
|
||||
char *name;
|
||||
char *val;
|
||||
nvlist_t *nvl;
|
||||
};
|
||||
|
||||
struct printc {
|
||||
int active_colsz_def;
|
||||
int be_colsz;
|
||||
@ -324,6 +330,74 @@ print_headers(nvlist_t *props, struct printc *pc)
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Sort the given nvlist of boot environments by property.
|
||||
*/
|
||||
static int
|
||||
prop_list_sort(nvlist_t *props, char *property, bool reverse)
|
||||
{
|
||||
nvpair_t *nvp;
|
||||
nvlist_t *nvl;
|
||||
int i, nvp_count;
|
||||
uint64_t lval, rval;
|
||||
struct sort_column sc_prev, sc_next;
|
||||
|
||||
/* a temporary list to work with */
|
||||
nvlist_dup(props, &nvl, 0);
|
||||
|
||||
nvp_count = fnvlist_num_pairs(nvl);
|
||||
for (i = 0; i < nvp_count; i++) {
|
||||
|
||||
nvp = nvlist_next_nvpair(nvl, NULL);
|
||||
nvpair_value_nvlist(nvp, &sc_prev.nvl);
|
||||
nvlist_lookup_string(sc_prev.nvl, "name", &sc_prev.name);
|
||||
nvlist_lookup_string(sc_prev.nvl, property, &sc_prev.val);
|
||||
|
||||
while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
|
||||
|
||||
nvpair_value_nvlist(nvp, &sc_next.nvl);
|
||||
nvlist_lookup_string(sc_next.nvl, "name", &sc_next.name);
|
||||
nvlist_lookup_string(sc_next.nvl, property, &sc_next.val);
|
||||
|
||||
/* properties that use numerical comparison */
|
||||
if (strcmp(property, "creation") == 0 ||
|
||||
strcmp(property, "used") == 0 ||
|
||||
strcmp(property, "usedds") == 0 ||
|
||||
strcmp(property, "usedsnap") == 0 ||
|
||||
strcmp(property, "usedrefreserv") == 0) {
|
||||
|
||||
lval = strtoull(sc_prev.val, NULL, 10);
|
||||
rval = strtoull(sc_next.val, NULL, 10);
|
||||
|
||||
if ((lval < rval && reverse) ||
|
||||
(lval > rval && !reverse))
|
||||
sc_prev = sc_next;
|
||||
}
|
||||
|
||||
/* properties that use string comparison */
|
||||
else if (strcmp(property, "name") == 0 ||
|
||||
strcmp(property, "origin") == 0) {
|
||||
if ((strcmp(sc_prev.val, sc_next.val) < 0 && reverse) ||
|
||||
(strcmp(sc_prev.val, sc_next.val) > 0 && !reverse))
|
||||
sc_prev = sc_next;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The 'props' nvlist has been created to only have unique names.
|
||||
* When a name is added, any existing nvlist's with the same name
|
||||
* will be removed. Eventually, all existing nvlist's are replaced
|
||||
* in sorted order.
|
||||
*/
|
||||
nvlist_add_nvlist(props, sc_prev.name, sc_prev.nvl);
|
||||
nvlist_remove_all(nvl, sc_prev.name);
|
||||
}
|
||||
|
||||
be_prop_list_free(nvl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
bectl_cmd_list(int argc, char *argv[])
|
||||
{
|
||||
@ -331,12 +405,14 @@ bectl_cmd_list(int argc, char *argv[])
|
||||
nvpair_t *cur;
|
||||
nvlist_t *dsprops, *props;
|
||||
int opt, printed;
|
||||
boolean_t active_now, active_reboot;
|
||||
char *column;
|
||||
bool reverse;
|
||||
|
||||
column = NULL;
|
||||
props = NULL;
|
||||
printed = 0;
|
||||
bzero(&pc, sizeof(pc));
|
||||
while ((opt = getopt(argc, argv, "aDHs")) != -1) {
|
||||
while ((opt = getopt(argc, argv, "aDHsc:C:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'a':
|
||||
pc.show_all_datasets = true;
|
||||
@ -350,6 +426,18 @@ bectl_cmd_list(int argc, char *argv[])
|
||||
case 's':
|
||||
pc.show_snaps = true;
|
||||
break;
|
||||
case 'c':
|
||||
if (column != NULL)
|
||||
free(column);
|
||||
column = strdup(optarg);
|
||||
reverse = false;
|
||||
break;
|
||||
case 'C':
|
||||
if (column != NULL)
|
||||
free(column);
|
||||
column = strdup(optarg);
|
||||
reverse = true;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "bectl list: unknown option '-%c'\n",
|
||||
optopt);
|
||||
@ -374,44 +462,33 @@ bectl_cmd_list(int argc, char *argv[])
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* List boot environments in alphabetical order by default */
|
||||
if (column == NULL) {
|
||||
column = strdup("name");
|
||||
reverse = false;
|
||||
}
|
||||
|
||||
prop_list_sort(props, column, reverse);
|
||||
|
||||
/* Force -D off if either -a or -s are specified */
|
||||
if (pc.show_all_datasets || pc.show_snaps)
|
||||
pc.show_space = false;
|
||||
if (!pc.script_fmt)
|
||||
print_headers(props, &pc);
|
||||
/* Do a first pass to print active and next active first */
|
||||
|
||||
/* Print boot environments */
|
||||
for (cur = nvlist_next_nvpair(props, NULL); cur != NULL;
|
||||
cur = nvlist_next_nvpair(props, cur)) {
|
||||
nvpair_value_nvlist(cur, &dsprops);
|
||||
active_now = active_reboot = false;
|
||||
|
||||
nvlist_lookup_boolean_value(dsprops, "active", &active_now);
|
||||
nvlist_lookup_boolean_value(dsprops, "nextboot",
|
||||
&active_reboot);
|
||||
if (!active_now && !active_reboot)
|
||||
continue;
|
||||
if (printed > 0 && (pc.show_all_datasets || pc.show_snaps))
|
||||
printf("\n");
|
||||
|
||||
print_info(nvpair_name(cur), dsprops, &pc);
|
||||
printed++;
|
||||
}
|
||||
|
||||
/* Now pull everything else */
|
||||
for (cur = nvlist_next_nvpair(props, NULL); cur != NULL;
|
||||
cur = nvlist_next_nvpair(props, cur)) {
|
||||
nvpair_value_nvlist(cur, &dsprops);
|
||||
active_now = active_reboot = false;
|
||||
|
||||
nvlist_lookup_boolean_value(dsprops, "active", &active_now);
|
||||
nvlist_lookup_boolean_value(dsprops, "nextboot",
|
||||
&active_reboot);
|
||||
if (active_now || active_reboot)
|
||||
continue;
|
||||
if (printed > 0 && (pc.show_all_datasets || pc.show_snaps))
|
||||
printf("\n");
|
||||
print_info(nvpair_name(cur), dsprops, &pc);
|
||||
printed++;
|
||||
}
|
||||
free(column);
|
||||
be_prop_list_free(props);
|
||||
|
||||
return (0);
|
||||
|
Loading…
Reference in New Issue
Block a user