1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-12-19 10:53:58 +00:00

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:
Kyle Evans 2019-09-04 13:59:06 +00:00
parent c2a13d6f24
commit f0298be018
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=351813
3 changed files with 148 additions and 40 deletions

View File

@ -18,7 +18,7 @@
.\" .\"
.\" $FreeBSD$ .\" $FreeBSD$
.\" .\"
.Dd May 12, 2019 .Dd September 4, 2019
.Dt BECTL 8 .Dt BECTL 8
.Os .Os
.Sh NAME .Sh NAME
@ -57,6 +57,9 @@
.Nm .Nm
.Cm list .Cm list
.Op Fl aDHs .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 .Nm
.Cm mount .Cm mount
.Ar beName .Ar beName
@ -234,7 +237,12 @@ generated by
.El .El
.Pp .Pp
All default parameters may be overwritten. 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. Display all boot environments.
The The
.Em Active .Em Active
@ -245,21 +253,44 @@ active on reboot
or both or both
.Pq Em \&NR . .Pq Em \&NR .
.Pp .Pp
If .Bl -tag -width indent
.Fl a .It Fl a
is used, display all datasets. Display all datasets.
If .It Fl D
.Fl D Display the full space usage for each boot environment, assuming all
is used, display the full space usage for each boot environment, assuming all
other boot environments were destroyed. other boot environments were destroyed.
The .It Fl H
.Fl H Used for scripting.
option is used for scripting. Do not print headers and separate fields by a single tab instead of
It does not print headers and separate fields by a single tab instead of
arbitrary white space. 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 .Fl s
is used, display all snapshots as well. or
.Fl a
option is used.
.It Cm mount Ar beName Op Ar mountpoint .It Cm mount Ar beName Op Ar mountpoint
Temporarily mount the boot environment. Temporarily mount the boot environment.
Mount at the specified Mount at the specified

View File

@ -80,7 +80,7 @@ usage(bool explicit)
"\tbectl jail {-b | -U} [{-o key=value | -u key}]... " "\tbectl jail {-b | -U} [{-o key=value | -u key}]... "
"{jailID | jailName}\n" "{jailID | jailName}\n"
"\t bootenv [utility [argument ...]]\n" "\t bootenv [utility [argument ...]]\n"
"\tbectl list [-DHas]\n" "\tbectl list [-DHas] [{-c property | -C property}]\n"
"\tbectl mount beName [mountpoint]\n" "\tbectl mount beName [mountpoint]\n"
"\tbectl rename origBeName newBeName\n" "\tbectl rename origBeName newBeName\n"
"\tbectl {ujail | unjail} {jailID | jailName} bootenv\n" "\tbectl {ujail | unjail} {jailID | jailName} bootenv\n"

View File

@ -38,6 +38,12 @@ __FBSDID("$FreeBSD$");
#include "bectl.h" #include "bectl.h"
struct sort_column {
char *name;
char *val;
nvlist_t *nvl;
};
struct printc { struct printc {
int active_colsz_def; int active_colsz_def;
int be_colsz; int be_colsz;
@ -324,6 +330,74 @@ print_headers(nvlist_t *props, struct printc *pc)
printf("\n"); 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 int
bectl_cmd_list(int argc, char *argv[]) bectl_cmd_list(int argc, char *argv[])
{ {
@ -331,12 +405,14 @@ bectl_cmd_list(int argc, char *argv[])
nvpair_t *cur; nvpair_t *cur;
nvlist_t *dsprops, *props; nvlist_t *dsprops, *props;
int opt, printed; int opt, printed;
boolean_t active_now, active_reboot; char *column;
bool reverse;
column = NULL;
props = NULL; props = NULL;
printed = 0; printed = 0;
bzero(&pc, sizeof(pc)); bzero(&pc, sizeof(pc));
while ((opt = getopt(argc, argv, "aDHs")) != -1) { while ((opt = getopt(argc, argv, "aDHsc:C:")) != -1) {
switch (opt) { switch (opt) {
case 'a': case 'a':
pc.show_all_datasets = true; pc.show_all_datasets = true;
@ -350,6 +426,18 @@ bectl_cmd_list(int argc, char *argv[])
case 's': case 's':
pc.show_snaps = true; pc.show_snaps = true;
break; 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: default:
fprintf(stderr, "bectl list: unknown option '-%c'\n", fprintf(stderr, "bectl list: unknown option '-%c'\n",
optopt); optopt);
@ -374,44 +462,33 @@ bectl_cmd_list(int argc, char *argv[])
return (1); 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 */ /* Force -D off if either -a or -s are specified */
if (pc.show_all_datasets || pc.show_snaps) if (pc.show_all_datasets || pc.show_snaps)
pc.show_space = false; pc.show_space = false;
if (!pc.script_fmt) if (!pc.script_fmt)
print_headers(props, &pc); 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; for (cur = nvlist_next_nvpair(props, NULL); cur != NULL;
cur = nvlist_next_nvpair(props, cur)) { cur = nvlist_next_nvpair(props, cur)) {
nvpair_value_nvlist(cur, &dsprops); 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)) if (printed > 0 && (pc.show_all_datasets || pc.show_snaps))
printf("\n"); printf("\n");
print_info(nvpair_name(cur), dsprops, &pc); print_info(nvpair_name(cur), dsprops, &pc);
printed++; printed++;
} }
/* Now pull everything else */ free(column);
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++;
}
be_prop_list_free(props); be_prop_list_free(props);
return (0); return (0);