Skip to content

Commit 443abfc

Browse files
Umer Saleembehlendorf
authored andcommitted
JSON output support for zfs list
This commit adds support for JSON output for zfs list using '-j' option. Information is collected in JSON format which is later printed in jSON format. Existing options for zfs list also work with '-j'. man pages are updated with relevant information. Reviewed-by: Tony Hutter <[email protected]> Reviewed-by: Ameer Hamza <[email protected]> Signed-off-by: Umer Saleem <[email protected]> Closes #16217
1 parent aa15b60 commit 443abfc

File tree

2 files changed

+296
-44
lines changed

2 files changed

+296
-44
lines changed

cmd/zfs/zfs_main.c

Lines changed: 135 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -310,8 +310,9 @@ get_usage(zfs_help_t idx)
310310
return (gettext("\tupgrade [-v]\n"
311311
"\tupgrade [-r] [-V version] <-a | filesystem ...>\n"));
312312
case HELP_LIST:
313-
return (gettext("\tlist [-Hp] [-r|-d max] [-o property[,...]] "
314-
"[-s property]...\n\t [-S property]... [-t type[,...]] "
313+
return (gettext("\tlist [-Hp] [-j [--json-int]] [-r|-d max] "
314+
"[-o property[,...]] [-s property]...\n\t "
315+
"[-S property]... [-t type[,...]] "
315316
"[filesystem|volume|snapshot] ...\n"));
316317
case HELP_MOUNT:
317318
return (gettext("\tmount\n"
@@ -3609,6 +3610,9 @@ typedef struct list_cbdata {
36093610
boolean_t cb_literal;
36103611
boolean_t cb_scripted;
36113612
zprop_list_t *cb_proplist;
3613+
boolean_t cb_json;
3614+
nvlist_t *cb_jsobj;
3615+
boolean_t cb_json_as_int;
36123616
} list_cbdata_t;
36133617

36143618
/*
@@ -3679,10 +3683,11 @@ zfs_list_avail_color(zfs_handle_t *zhp)
36793683

36803684
/*
36813685
* Given a dataset and a list of fields, print out all the properties according
3682-
* to the described layout.
3686+
* to the described layout, or return an nvlist containing all the fields, later
3687+
* to be printed out as JSON object.
36833688
*/
36843689
static void
3685-
print_dataset(zfs_handle_t *zhp, list_cbdata_t *cb)
3690+
collect_dataset(zfs_handle_t *zhp, list_cbdata_t *cb)
36863691
{
36873692
zprop_list_t *pl = cb->cb_proplist;
36883693
boolean_t first = B_TRUE;
@@ -3691,9 +3696,23 @@ print_dataset(zfs_handle_t *zhp, list_cbdata_t *cb)
36913696
nvlist_t *propval;
36923697
const char *propstr;
36933698
boolean_t right_justify;
3699+
nvlist_t *item, *d, *props;
3700+
item = d = props = NULL;
3701+
zprop_source_t sourcetype = ZPROP_SRC_NONE;
3702+
char source[ZFS_MAX_DATASET_NAME_LEN];
3703+
if (cb->cb_json) {
3704+
d = fnvlist_lookup_nvlist(cb->cb_jsobj, "datasets");
3705+
if (d == NULL) {
3706+
fprintf(stderr, "datasets obj not found.\n");
3707+
exit(1);
3708+
}
3709+
item = fnvlist_alloc();
3710+
props = fnvlist_alloc();
3711+
fill_dataset_info(item, zhp, cb->cb_json_as_int);
3712+
}
36943713

36953714
for (; pl != NULL; pl = pl->pl_next) {
3696-
if (!first) {
3715+
if (!cb->cb_json && !first) {
36973716
if (cb->cb_scripted)
36983717
(void) putchar('\t');
36993718
else
@@ -3709,69 +3728,112 @@ print_dataset(zfs_handle_t *zhp, list_cbdata_t *cb)
37093728
right_justify = zfs_prop_align_right(pl->pl_prop);
37103729
} else if (pl->pl_prop != ZPROP_USERPROP) {
37113730
if (zfs_prop_get(zhp, pl->pl_prop, property,
3712-
sizeof (property), NULL, NULL, 0,
3713-
cb->cb_literal) != 0)
3731+
sizeof (property), &sourcetype, source,
3732+
sizeof (source), cb->cb_literal) != 0)
37143733
propstr = "-";
37153734
else
37163735
propstr = property;
37173736
right_justify = zfs_prop_align_right(pl->pl_prop);
37183737
} else if (zfs_prop_userquota(pl->pl_user_prop)) {
3738+
sourcetype = ZPROP_SRC_LOCAL;
37193739
if (zfs_prop_get_userquota(zhp, pl->pl_user_prop,
3720-
property, sizeof (property), cb->cb_literal) != 0)
3740+
property, sizeof (property), cb->cb_literal) != 0) {
3741+
sourcetype = ZPROP_SRC_NONE;
37213742
propstr = "-";
3722-
else
3743+
} else {
37233744
propstr = property;
3745+
}
37243746
right_justify = B_TRUE;
37253747
} else if (zfs_prop_written(pl->pl_user_prop)) {
3748+
sourcetype = ZPROP_SRC_LOCAL;
37263749
if (zfs_prop_get_written(zhp, pl->pl_user_prop,
3727-
property, sizeof (property), cb->cb_literal) != 0)
3750+
property, sizeof (property), cb->cb_literal) != 0) {
3751+
sourcetype = ZPROP_SRC_NONE;
37283752
propstr = "-";
3729-
else
3753+
} else {
37303754
propstr = property;
3755+
}
37313756
right_justify = B_TRUE;
37323757
} else {
37333758
if (nvlist_lookup_nvlist(userprops,
3734-
pl->pl_user_prop, &propval) != 0)
3759+
pl->pl_user_prop, &propval) != 0) {
37353760
propstr = "-";
3736-
else
3761+
} else {
37373762
propstr = fnvlist_lookup_string(propval,
37383763
ZPROP_VALUE);
3764+
strlcpy(source,
3765+
fnvlist_lookup_string(propval,
3766+
ZPROP_SOURCE), ZFS_MAX_DATASET_NAME_LEN);
3767+
if (strcmp(source,
3768+
zfs_get_name(zhp)) == 0) {
3769+
sourcetype = ZPROP_SRC_LOCAL;
3770+
} else if (strcmp(source,
3771+
ZPROP_SOURCE_VAL_RECVD) == 0) {
3772+
sourcetype = ZPROP_SRC_RECEIVED;
3773+
} else {
3774+
sourcetype = ZPROP_SRC_INHERITED;
3775+
}
3776+
}
37393777
right_justify = B_FALSE;
37403778
}
37413779

3742-
/*
3743-
* zfs_list_avail_color() needs ZFS_PROP_AVAILABLE + USED
3744-
* - so we need another for() search for the USED part
3745-
* - when no colors wanted, we can skip the whole thing
3746-
*/
3747-
if (use_color() && pl->pl_prop == ZFS_PROP_AVAILABLE) {
3748-
zprop_list_t *pl2 = cb->cb_proplist;
3749-
for (; pl2 != NULL; pl2 = pl2->pl_next) {
3750-
if (pl2->pl_prop == ZFS_PROP_USED) {
3751-
color_start(zfs_list_avail_color(zhp));
3752-
/* found it, no need for more loops */
3753-
break;
3780+
if (cb->cb_json) {
3781+
if (pl->pl_prop == ZFS_PROP_NAME)
3782+
continue;
3783+
if (zprop_nvlist_one_property(
3784+
zfs_prop_to_name(pl->pl_prop), propstr,
3785+
sourcetype, source, NULL, props,
3786+
cb->cb_json_as_int) != 0)
3787+
nomem();
3788+
} else {
3789+
/*
3790+
* zfs_list_avail_color() needs
3791+
* ZFS_PROP_AVAILABLE + USED, so we need another
3792+
* for() search for the USED part when no colors
3793+
* wanted, we can skip the whole thing
3794+
*/
3795+
if (use_color() && pl->pl_prop == ZFS_PROP_AVAILABLE) {
3796+
zprop_list_t *pl2 = cb->cb_proplist;
3797+
for (; pl2 != NULL; pl2 = pl2->pl_next) {
3798+
if (pl2->pl_prop == ZFS_PROP_USED) {
3799+
color_start(
3800+
zfs_list_avail_color(zhp));
3801+
/*
3802+
* found it, no need for more
3803+
* loops
3804+
*/
3805+
break;
3806+
}
37543807
}
37553808
}
3756-
}
37573809

3758-
/*
3759-
* If this is being called in scripted mode, or if this is the
3760-
* last column and it is left-justified, don't include a width
3761-
* format specifier.
3762-
*/
3763-
if (cb->cb_scripted || (pl->pl_next == NULL && !right_justify))
3764-
(void) fputs(propstr, stdout);
3765-
else if (right_justify)
3766-
(void) printf("%*s", (int)pl->pl_width, propstr);
3767-
else
3768-
(void) printf("%-*s", (int)pl->pl_width, propstr);
3810+
/*
3811+
* If this is being called in scripted mode, or if
3812+
* this is the last column and it is left-justified,
3813+
* don't include a width format specifier.
3814+
*/
3815+
if (cb->cb_scripted || (pl->pl_next == NULL &&
3816+
!right_justify))
3817+
(void) fputs(propstr, stdout);
3818+
else if (right_justify) {
3819+
(void) printf("%*s", (int)pl->pl_width,
3820+
propstr);
3821+
} else {
3822+
(void) printf("%-*s", (int)pl->pl_width,
3823+
propstr);
3824+
}
37693825

3770-
if (pl->pl_prop == ZFS_PROP_AVAILABLE)
3771-
color_end();
3826+
if (pl->pl_prop == ZFS_PROP_AVAILABLE)
3827+
color_end();
3828+
}
37723829
}
3773-
3774-
(void) putchar('\n');
3830+
if (cb->cb_json) {
3831+
fnvlist_add_nvlist(item, "properties", props);
3832+
fnvlist_add_nvlist(d, zfs_get_name(zhp), item);
3833+
fnvlist_free(props);
3834+
fnvlist_free(item);
3835+
} else
3836+
(void) putchar('\n');
37753837
}
37763838

37773839
/*
@@ -3783,12 +3845,12 @@ list_callback(zfs_handle_t *zhp, void *data)
37833845
list_cbdata_t *cbp = data;
37843846

37853847
if (cbp->cb_first) {
3786-
if (!cbp->cb_scripted)
3848+
if (!cbp->cb_scripted && !cbp->cb_json)
37873849
print_header(cbp);
37883850
cbp->cb_first = B_FALSE;
37893851
}
37903852

3791-
print_dataset(zhp, cbp);
3853+
collect_dataset(zhp, cbp);
37923854

37933855
return (0);
37943856
}
@@ -3807,9 +3869,16 @@ zfs_do_list(int argc, char **argv)
38073869
int ret = 0;
38083870
zfs_sort_column_t *sortcol = NULL;
38093871
int flags = ZFS_ITER_PROP_LISTSNAPS | ZFS_ITER_ARGS_CAN_BE_PATHS;
3872+
nvlist_t *data = NULL;
3873+
3874+
struct option long_options[] = {
3875+
{"json-int", no_argument, NULL, ZFS_OPTION_JSON_NUMS_AS_INT},
3876+
{0, 0, 0, 0}
3877+
};
38103878

38113879
/* check options */
3812-
while ((c = getopt(argc, argv, "HS:d:o:prs:t:")) != -1) {
3880+
while ((c = getopt_long(argc, argv, "jHS:d:o:prs:t:", long_options,
3881+
NULL)) != -1) {
38133882
switch (c) {
38143883
case 'o':
38153884
fields = optarg;
@@ -3824,6 +3893,17 @@ zfs_do_list(int argc, char **argv)
38243893
case 'r':
38253894
flags |= ZFS_ITER_RECURSE;
38263895
break;
3896+
case 'j':
3897+
cb.cb_json = B_TRUE;
3898+
cb.cb_jsobj = zfs_json_schema(0, 1);
3899+
data = fnvlist_alloc();
3900+
fnvlist_add_nvlist(cb.cb_jsobj, "datasets", data);
3901+
fnvlist_free(data);
3902+
break;
3903+
case ZFS_OPTION_JSON_NUMS_AS_INT:
3904+
cb.cb_json_as_int = B_TRUE;
3905+
cb.cb_literal = B_TRUE;
3906+
break;
38273907
case 'H':
38283908
cb.cb_scripted = B_TRUE;
38293909
break;
@@ -3897,6 +3977,12 @@ found3:;
38973977
argc -= optind;
38983978
argv += optind;
38993979

3980+
if (!cb.cb_json && cb.cb_json_as_int) {
3981+
(void) fprintf(stderr, gettext("'--json-int' only works with"
3982+
" '-j' option\n"));
3983+
usage(B_FALSE);
3984+
}
3985+
39003986
/*
39013987
* If "-o space" and no types were specified, don't display snapshots.
39023988
*/
@@ -3936,6 +4022,11 @@ found3:;
39364022
ret = zfs_for_each(argc, argv, flags, types, sortcol, &cb.cb_proplist,
39374023
limit, list_callback, &cb);
39384024

4025+
if (ret == 0 && cb.cb_json)
4026+
zcmd_print_json(cb.cb_jsobj);
4027+
else if (ret != 0 && cb.cb_json)
4028+
nvlist_free(cb.cb_jsobj);
4029+
39394030
zprop_free_list(cb.cb_proplist);
39404031
zfs_free_sort_columns(sortcol);
39414032

0 commit comments

Comments
 (0)