mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2024-11-17 10:01:01 +03:00
JSON output support for zpool list
This commit adds support for zpool list command to output the list of ZFS pools in JSON format using '-j' option.. Information about available pools is collected in nvlist which is later printed to stdout in JSON format. Existing options for zfs list command work with '-j' flag. man page for zpool list is updated accordingly. Reviewed-by: Tony Hutter <hutter2@llnl.gov> Reviewed-by: Ameer Hamza <ahamza@ixsystems.com> Signed-off-by: Umer Saleem <usaleem@ixsystems.com> Closes #16217
This commit is contained in:
parent
eb2b824bde
commit
4e6b3f7e1d
@ -403,8 +403,8 @@ get_usage(zpool_help_t idx)
|
|||||||
return (gettext("\tlabelclear [-f] <vdev>\n"));
|
return (gettext("\tlabelclear [-f] <vdev>\n"));
|
||||||
case HELP_LIST:
|
case HELP_LIST:
|
||||||
return (gettext("\tlist [-gHLpPv] [-o property[,...]] "
|
return (gettext("\tlist [-gHLpPv] [-o property[,...]] "
|
||||||
"[-T d|u] [pool] ... \n"
|
"[-j [--json-int, --json-pool-key-guid]]\n"
|
||||||
"\t [interval [count]]\n"));
|
"\t [-T d|u] [pool] [interval [count]]\n"));
|
||||||
case HELP_PREFETCH:
|
case HELP_PREFETCH:
|
||||||
return (gettext("\tprefetch -t <type> [<type opts>] <pool>\n"
|
return (gettext("\tprefetch -t <type> [<type opts>] <pool>\n"
|
||||||
"\t -t ddt <pool>\n"));
|
"\t -t ddt <pool>\n"));
|
||||||
@ -968,13 +968,14 @@ fill_pool_info(nvlist_t *list, zpool_handle_t *zhp, boolean_t addtype,
|
|||||||
|
|
||||||
static void
|
static void
|
||||||
fill_vdev_info(nvlist_t *list, zpool_handle_t *zhp, char *name,
|
fill_vdev_info(nvlist_t *list, zpool_handle_t *zhp, char *name,
|
||||||
boolean_t as_int)
|
boolean_t addtype, boolean_t as_int)
|
||||||
{
|
{
|
||||||
boolean_t spare, l2c, log;
|
boolean_t spare, l2c, log;
|
||||||
const char *path, *phys, *devid;
|
const char *path, *phys, *devid;
|
||||||
nvlist_t *nvdev = zpool_find_vdev(zhp, name, &spare, &l2c, &log);
|
nvlist_t *nvdev = zpool_find_vdev(zhp, name, &spare, &l2c, &log);
|
||||||
|
|
||||||
fnvlist_add_string(list, "name", name);
|
fnvlist_add_string(list, "name", name);
|
||||||
|
if (addtype)
|
||||||
fnvlist_add_string(list, "type", "VDEV");
|
fnvlist_add_string(list, "type", "VDEV");
|
||||||
if (nvdev) {
|
if (nvdev) {
|
||||||
const char *type = fnvlist_lookup_string(nvdev,
|
const char *type = fnvlist_lookup_string(nvdev,
|
||||||
@ -6483,9 +6484,13 @@ typedef struct list_cbdata {
|
|||||||
boolean_t cb_verbose;
|
boolean_t cb_verbose;
|
||||||
int cb_name_flags;
|
int cb_name_flags;
|
||||||
int cb_namewidth;
|
int cb_namewidth;
|
||||||
|
boolean_t cb_json;
|
||||||
boolean_t cb_scripted;
|
boolean_t cb_scripted;
|
||||||
zprop_list_t *cb_proplist;
|
zprop_list_t *cb_proplist;
|
||||||
boolean_t cb_literal;
|
boolean_t cb_literal;
|
||||||
|
nvlist_t *cb_jsobj;
|
||||||
|
boolean_t cb_json_as_int;
|
||||||
|
boolean_t cb_json_pool_key_guid;
|
||||||
} list_cbdata_t;
|
} list_cbdata_t;
|
||||||
|
|
||||||
|
|
||||||
@ -6546,7 +6551,7 @@ print_header(list_cbdata_t *cb)
|
|||||||
* to the described layout. Used by zpool_do_list().
|
* to the described layout. Used by zpool_do_list().
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
print_pool(zpool_handle_t *zhp, list_cbdata_t *cb)
|
collect_pool(zpool_handle_t *zhp, list_cbdata_t *cb)
|
||||||
{
|
{
|
||||||
zprop_list_t *pl = cb->cb_proplist;
|
zprop_list_t *pl = cb->cb_proplist;
|
||||||
boolean_t first = B_TRUE;
|
boolean_t first = B_TRUE;
|
||||||
@ -6554,6 +6559,20 @@ print_pool(zpool_handle_t *zhp, list_cbdata_t *cb)
|
|||||||
const char *propstr;
|
const char *propstr;
|
||||||
boolean_t right_justify;
|
boolean_t right_justify;
|
||||||
size_t width;
|
size_t width;
|
||||||
|
zprop_source_t sourcetype = ZPROP_SRC_NONE;
|
||||||
|
nvlist_t *item, *d, *props;
|
||||||
|
item = d = props = NULL;
|
||||||
|
|
||||||
|
if (cb->cb_json) {
|
||||||
|
item = fnvlist_alloc();
|
||||||
|
props = fnvlist_alloc();
|
||||||
|
d = fnvlist_lookup_nvlist(cb->cb_jsobj, "pools");
|
||||||
|
if (d == NULL) {
|
||||||
|
fprintf(stderr, "pools obj not found.\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
fill_pool_info(item, zhp, B_TRUE, cb->cb_json_as_int);
|
||||||
|
}
|
||||||
|
|
||||||
for (; pl != NULL; pl = pl->pl_next) {
|
for (; pl != NULL; pl = pl->pl_next) {
|
||||||
|
|
||||||
@ -6566,7 +6585,7 @@ print_pool(zpool_handle_t *zhp, list_cbdata_t *cb)
|
|||||||
width = cb->cb_namewidth;
|
width = cb->cb_namewidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!first) {
|
if (!cb->cb_json && !first) {
|
||||||
if (cb->cb_scripted)
|
if (cb->cb_scripted)
|
||||||
(void) fputc('\t', stdout);
|
(void) fputc('\t', stdout);
|
||||||
else
|
else
|
||||||
@ -6578,7 +6597,8 @@ print_pool(zpool_handle_t *zhp, list_cbdata_t *cb)
|
|||||||
right_justify = B_FALSE;
|
right_justify = B_FALSE;
|
||||||
if (pl->pl_prop != ZPROP_USERPROP) {
|
if (pl->pl_prop != ZPROP_USERPROP) {
|
||||||
if (zpool_get_prop(zhp, pl->pl_prop, property,
|
if (zpool_get_prop(zhp, pl->pl_prop, property,
|
||||||
sizeof (property), NULL, cb->cb_literal) != 0)
|
sizeof (property), &sourcetype,
|
||||||
|
cb->cb_literal) != 0)
|
||||||
propstr = "-";
|
propstr = "-";
|
||||||
else
|
else
|
||||||
propstr = property;
|
propstr = property;
|
||||||
@ -6589,33 +6609,61 @@ print_pool(zpool_handle_t *zhp, list_cbdata_t *cb)
|
|||||||
zpool_prop_get_feature(zhp, pl->pl_user_prop, property,
|
zpool_prop_get_feature(zhp, pl->pl_user_prop, property,
|
||||||
sizeof (property)) == 0) {
|
sizeof (property)) == 0) {
|
||||||
propstr = property;
|
propstr = property;
|
||||||
|
sourcetype = ZPROP_SRC_LOCAL;
|
||||||
} else if (zfs_prop_user(pl->pl_user_prop) &&
|
} else if (zfs_prop_user(pl->pl_user_prop) &&
|
||||||
zpool_get_userprop(zhp, pl->pl_user_prop, property,
|
zpool_get_userprop(zhp, pl->pl_user_prop, property,
|
||||||
sizeof (property), NULL) == 0) {
|
sizeof (property), &sourcetype) == 0) {
|
||||||
propstr = property;
|
propstr = property;
|
||||||
} else {
|
} else {
|
||||||
propstr = "-";
|
propstr = "-";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cb->cb_json) {
|
||||||
|
if (pl->pl_prop == ZPOOL_PROP_NAME)
|
||||||
|
continue;
|
||||||
|
(void) zprop_nvlist_one_property(
|
||||||
|
zpool_prop_to_name(pl->pl_prop), propstr,
|
||||||
|
sourcetype, NULL, NULL, props, cb->cb_json_as_int);
|
||||||
|
} else {
|
||||||
/*
|
/*
|
||||||
* If this is being called in scripted mode, or if this is the
|
* If this is being called in scripted mode, or if this
|
||||||
* last column and it is left-justified, don't include a width
|
* is the last column and it is left-justified, don't
|
||||||
* format specifier.
|
* include a width format specifier.
|
||||||
*/
|
*/
|
||||||
if (cb->cb_scripted || (pl->pl_next == NULL && !right_justify))
|
if (cb->cb_scripted || (pl->pl_next == NULL &&
|
||||||
|
!right_justify))
|
||||||
(void) fputs(propstr, stdout);
|
(void) fputs(propstr, stdout);
|
||||||
else if (right_justify)
|
else if (right_justify)
|
||||||
(void) printf("%*s", (int)width, propstr);
|
(void) printf("%*s", (int)width, propstr);
|
||||||
else
|
else
|
||||||
(void) printf("%-*s", (int)width, propstr);
|
(void) printf("%-*s", (int)width, propstr);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cb->cb_json) {
|
||||||
|
fnvlist_add_nvlist(item, "properties", props);
|
||||||
|
if (cb->cb_json_pool_key_guid) {
|
||||||
|
char pool_guid[256];
|
||||||
|
uint64_t guid = fnvlist_lookup_uint64(
|
||||||
|
zpool_get_config(zhp, NULL),
|
||||||
|
ZPOOL_CONFIG_POOL_GUID);
|
||||||
|
snprintf(pool_guid, 256, "%llu",
|
||||||
|
(u_longlong_t)guid);
|
||||||
|
fnvlist_add_nvlist(d, pool_guid, item);
|
||||||
|
} else {
|
||||||
|
fnvlist_add_nvlist(d, zpool_get_name(zhp),
|
||||||
|
item);
|
||||||
|
}
|
||||||
|
fnvlist_free(props);
|
||||||
|
fnvlist_free(item);
|
||||||
|
} else
|
||||||
(void) fputc('\n', stdout);
|
(void) fputc('\n', stdout);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
print_one_column(zpool_prop_t prop, uint64_t value, const char *str,
|
collect_vdev_prop(zpool_prop_t prop, uint64_t value, const char *str,
|
||||||
boolean_t scripted, boolean_t valid, enum zfs_nicenum_format format)
|
boolean_t scripted, boolean_t valid, enum zfs_nicenum_format format,
|
||||||
|
boolean_t json, nvlist_t *nvl, boolean_t as_int)
|
||||||
{
|
{
|
||||||
char propval[64];
|
char propval[64];
|
||||||
boolean_t fixed;
|
boolean_t fixed;
|
||||||
@ -6665,10 +6713,15 @@ print_one_column(zpool_prop_t prop, uint64_t value, const char *str,
|
|||||||
if (!valid)
|
if (!valid)
|
||||||
(void) strlcpy(propval, "-", sizeof (propval));
|
(void) strlcpy(propval, "-", sizeof (propval));
|
||||||
|
|
||||||
|
if (json) {
|
||||||
|
zprop_nvlist_one_property(zpool_prop_to_name(prop), propval,
|
||||||
|
ZPROP_SRC_NONE, NULL, NULL, nvl, as_int);
|
||||||
|
} else {
|
||||||
if (scripted)
|
if (scripted)
|
||||||
(void) printf("\t%s", propval);
|
(void) printf("\t%s", propval);
|
||||||
else
|
else
|
||||||
(void) printf(" %*s", (int)width, propval);
|
(void) printf(" %*s", (int)width, propval);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -6676,15 +6729,17 @@ print_one_column(zpool_prop_t prop, uint64_t value, const char *str,
|
|||||||
* not compatible with '-o' <proplist> option
|
* not compatible with '-o' <proplist> option
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
print_list_stats(zpool_handle_t *zhp, const char *name, nvlist_t *nv,
|
collect_list_stats(zpool_handle_t *zhp, const char *name, nvlist_t *nv,
|
||||||
list_cbdata_t *cb, int depth, boolean_t isspare)
|
list_cbdata_t *cb, int depth, boolean_t isspare, nvlist_t *item)
|
||||||
{
|
{
|
||||||
nvlist_t **child;
|
nvlist_t **child;
|
||||||
vdev_stat_t *vs;
|
vdev_stat_t *vs;
|
||||||
uint_t c, children;
|
uint_t c, children = 0;
|
||||||
char *vname;
|
char *vname;
|
||||||
boolean_t scripted = cb->cb_scripted;
|
boolean_t scripted = cb->cb_scripted;
|
||||||
uint64_t islog = B_FALSE;
|
uint64_t islog = B_FALSE;
|
||||||
|
nvlist_t *props, *ent, *ch, *obj, *l2c, *sp;
|
||||||
|
props = ent = ch = obj = sp = l2c = NULL;
|
||||||
const char *dashes = "%-*s - - - - "
|
const char *dashes = "%-*s - - - - "
|
||||||
"- - - - -\n";
|
"- - - - -\n";
|
||||||
|
|
||||||
@ -6705,13 +6760,21 @@ print_list_stats(zpool_handle_t *zhp, const char *name, nvlist_t *nv,
|
|||||||
if (strcmp(name, VDEV_TYPE_INDIRECT) == 0)
|
if (strcmp(name, VDEV_TYPE_INDIRECT) == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (cb->cb_json) {
|
||||||
|
props = fnvlist_alloc();
|
||||||
|
ent = fnvlist_alloc();
|
||||||
|
fill_vdev_info(ent, zhp, (char *)name, B_FALSE,
|
||||||
|
cb->cb_json_as_int);
|
||||||
|
} else {
|
||||||
if (scripted)
|
if (scripted)
|
||||||
(void) printf("\t%s", name);
|
(void) printf("\t%s", name);
|
||||||
else if (strlen(name) + depth > cb->cb_namewidth)
|
else if (strlen(name) + depth > cb->cb_namewidth)
|
||||||
(void) printf("%*s%s", depth, "", name);
|
(void) printf("%*s%s", depth, "", name);
|
||||||
else
|
else
|
||||||
(void) printf("%*s%s%*s", depth, "", name,
|
(void) printf("%*s%s%*s", depth, "", name,
|
||||||
(int)(cb->cb_namewidth - strlen(name) - depth), "");
|
(int)(cb->cb_namewidth - strlen(name) -
|
||||||
|
depth), "");
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Print the properties for the individual vdevs. Some
|
* Print the properties for the individual vdevs. Some
|
||||||
@ -6719,30 +6782,39 @@ print_list_stats(zpool_handle_t *zhp, const char *name, nvlist_t *nv,
|
|||||||
* 'toplevel' boolean value is passed to the print_one_column()
|
* 'toplevel' boolean value is passed to the print_one_column()
|
||||||
* to indicate that the value is valid.
|
* to indicate that the value is valid.
|
||||||
*/
|
*/
|
||||||
if (VDEV_STAT_VALID(vs_pspace, c) && vs->vs_pspace)
|
if (VDEV_STAT_VALID(vs_pspace, c) && vs->vs_pspace) {
|
||||||
print_one_column(ZPOOL_PROP_SIZE, vs->vs_pspace, NULL,
|
collect_vdev_prop(ZPOOL_PROP_SIZE, vs->vs_pspace, NULL,
|
||||||
scripted, B_TRUE, format);
|
scripted, B_TRUE, format, cb->cb_json, props,
|
||||||
else
|
cb->cb_json_as_int);
|
||||||
print_one_column(ZPOOL_PROP_SIZE, vs->vs_space, NULL,
|
} else {
|
||||||
scripted, toplevel, format);
|
collect_vdev_prop(ZPOOL_PROP_SIZE, vs->vs_space, NULL,
|
||||||
print_one_column(ZPOOL_PROP_ALLOCATED, vs->vs_alloc, NULL,
|
scripted, toplevel, format, cb->cb_json, props,
|
||||||
scripted, toplevel, format);
|
cb->cb_json_as_int);
|
||||||
print_one_column(ZPOOL_PROP_FREE, vs->vs_space - vs->vs_alloc,
|
}
|
||||||
NULL, scripted, toplevel, format);
|
collect_vdev_prop(ZPOOL_PROP_ALLOCATED, vs->vs_alloc, NULL,
|
||||||
print_one_column(ZPOOL_PROP_CHECKPOINT,
|
scripted, toplevel, format, cb->cb_json, props,
|
||||||
vs->vs_checkpoint_space, NULL, scripted, toplevel, format);
|
cb->cb_json_as_int);
|
||||||
print_one_column(ZPOOL_PROP_EXPANDSZ, vs->vs_esize, NULL,
|
collect_vdev_prop(ZPOOL_PROP_FREE, vs->vs_space - vs->vs_alloc,
|
||||||
scripted, B_TRUE, format);
|
NULL, scripted, toplevel, format, cb->cb_json, props,
|
||||||
print_one_column(ZPOOL_PROP_FRAGMENTATION,
|
cb->cb_json_as_int);
|
||||||
|
collect_vdev_prop(ZPOOL_PROP_CHECKPOINT,
|
||||||
|
vs->vs_checkpoint_space, NULL, scripted, toplevel, format,
|
||||||
|
cb->cb_json, props, cb->cb_json_as_int);
|
||||||
|
collect_vdev_prop(ZPOOL_PROP_EXPANDSZ, vs->vs_esize, NULL,
|
||||||
|
scripted, B_TRUE, format, cb->cb_json, props,
|
||||||
|
cb->cb_json_as_int);
|
||||||
|
collect_vdev_prop(ZPOOL_PROP_FRAGMENTATION,
|
||||||
vs->vs_fragmentation, NULL, scripted,
|
vs->vs_fragmentation, NULL, scripted,
|
||||||
(vs->vs_fragmentation != ZFS_FRAG_INVALID && toplevel),
|
(vs->vs_fragmentation != ZFS_FRAG_INVALID && toplevel),
|
||||||
format);
|
format, cb->cb_json, props, cb->cb_json_as_int);
|
||||||
cap = (vs->vs_space == 0) ? 0 :
|
cap = (vs->vs_space == 0) ? 0 :
|
||||||
(vs->vs_alloc * 10000 / vs->vs_space);
|
(vs->vs_alloc * 10000 / vs->vs_space);
|
||||||
print_one_column(ZPOOL_PROP_CAPACITY, cap, NULL,
|
collect_vdev_prop(ZPOOL_PROP_CAPACITY, cap, NULL,
|
||||||
scripted, toplevel, format);
|
scripted, toplevel, format, cb->cb_json, props,
|
||||||
print_one_column(ZPOOL_PROP_DEDUPRATIO, 0, NULL,
|
cb->cb_json_as_int);
|
||||||
scripted, toplevel, format);
|
collect_vdev_prop(ZPOOL_PROP_DEDUPRATIO, 0, NULL,
|
||||||
|
scripted, toplevel, format, cb->cb_json, props,
|
||||||
|
cb->cb_json_as_int);
|
||||||
state = zpool_state_to_name(vs->vs_state, vs->vs_aux);
|
state = zpool_state_to_name(vs->vs_state, vs->vs_aux);
|
||||||
if (isspare) {
|
if (isspare) {
|
||||||
if (vs->vs_aux == VDEV_AUX_SPARED)
|
if (vs->vs_aux == VDEV_AUX_SPARED)
|
||||||
@ -6750,14 +6822,28 @@ print_list_stats(zpool_handle_t *zhp, const char *name, nvlist_t *nv,
|
|||||||
else if (vs->vs_state == VDEV_STATE_HEALTHY)
|
else if (vs->vs_state == VDEV_STATE_HEALTHY)
|
||||||
state = "AVAIL";
|
state = "AVAIL";
|
||||||
}
|
}
|
||||||
print_one_column(ZPOOL_PROP_HEALTH, 0, state, scripted,
|
collect_vdev_prop(ZPOOL_PROP_HEALTH, 0, state, scripted,
|
||||||
B_TRUE, format);
|
B_TRUE, format, cb->cb_json, props, cb->cb_json_as_int);
|
||||||
|
|
||||||
|
if (cb->cb_json) {
|
||||||
|
fnvlist_add_nvlist(ent, "properties", props);
|
||||||
|
fnvlist_free(props);
|
||||||
|
} else
|
||||||
(void) fputc('\n', stdout);
|
(void) fputc('\n', stdout);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
|
if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
|
||||||
&child, &children) != 0)
|
&child, &children) != 0) {
|
||||||
|
if (cb->cb_json) {
|
||||||
|
fnvlist_add_nvlist(item, name, ent);
|
||||||
|
fnvlist_free(ent);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cb->cb_json) {
|
||||||
|
ch = fnvlist_alloc();
|
||||||
|
}
|
||||||
|
|
||||||
/* list the normal vdevs first */
|
/* list the normal vdevs first */
|
||||||
for (c = 0; c < children; c++) {
|
for (c = 0; c < children; c++) {
|
||||||
@ -6776,14 +6862,28 @@ print_list_stats(zpool_handle_t *zhp, const char *name, nvlist_t *nv,
|
|||||||
|
|
||||||
vname = zpool_vdev_name(g_zfs, zhp, child[c],
|
vname = zpool_vdev_name(g_zfs, zhp, child[c],
|
||||||
cb->cb_name_flags | VDEV_NAME_TYPE_ID);
|
cb->cb_name_flags | VDEV_NAME_TYPE_ID);
|
||||||
print_list_stats(zhp, vname, child[c], cb, depth + 2, B_FALSE);
|
|
||||||
|
if (name == NULL || cb->cb_json != B_TRUE)
|
||||||
|
collect_list_stats(zhp, vname, child[c], cb, depth + 2,
|
||||||
|
B_FALSE, item);
|
||||||
|
else if (cb->cb_json) {
|
||||||
|
collect_list_stats(zhp, vname, child[c], cb, depth + 2,
|
||||||
|
B_FALSE, ch);
|
||||||
|
}
|
||||||
free(vname);
|
free(vname);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cb->cb_json) {
|
||||||
|
if (!nvlist_empty(ch))
|
||||||
|
fnvlist_add_nvlist(ent, "vdevs", ch);
|
||||||
|
fnvlist_free(ch);
|
||||||
|
}
|
||||||
|
|
||||||
/* list the classes: 'logs', 'dedup', and 'special' */
|
/* list the classes: 'logs', 'dedup', and 'special' */
|
||||||
for (uint_t n = 0; n < ARRAY_SIZE(class_name); n++) {
|
for (uint_t n = 0; n < ARRAY_SIZE(class_name); n++) {
|
||||||
boolean_t printed = B_FALSE;
|
boolean_t printed = B_FALSE;
|
||||||
|
if (cb->cb_json)
|
||||||
|
obj = fnvlist_alloc();
|
||||||
for (c = 0; c < children; c++) {
|
for (c = 0; c < children; c++) {
|
||||||
const char *bias = NULL;
|
const char *bias = NULL;
|
||||||
const char *type = NULL;
|
const char *type = NULL;
|
||||||
@ -6802,7 +6902,7 @@ print_list_stats(zpool_handle_t *zhp, const char *name, nvlist_t *nv,
|
|||||||
if (!islog && strcmp(type, VDEV_TYPE_INDIRECT) == 0)
|
if (!islog && strcmp(type, VDEV_TYPE_INDIRECT) == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!printed) {
|
if (!printed && !cb->cb_json) {
|
||||||
/* LINTED E_SEC_PRINTF_VAR_FMT */
|
/* LINTED E_SEC_PRINTF_VAR_FMT */
|
||||||
(void) printf(dashes, cb->cb_namewidth,
|
(void) printf(dashes, cb->cb_namewidth,
|
||||||
class_name[n]);
|
class_name[n]);
|
||||||
@ -6810,36 +6910,64 @@ print_list_stats(zpool_handle_t *zhp, const char *name, nvlist_t *nv,
|
|||||||
}
|
}
|
||||||
vname = zpool_vdev_name(g_zfs, zhp, child[c],
|
vname = zpool_vdev_name(g_zfs, zhp, child[c],
|
||||||
cb->cb_name_flags | VDEV_NAME_TYPE_ID);
|
cb->cb_name_flags | VDEV_NAME_TYPE_ID);
|
||||||
print_list_stats(zhp, vname, child[c], cb, depth + 2,
|
collect_list_stats(zhp, vname, child[c], cb, depth + 2,
|
||||||
B_FALSE);
|
B_FALSE, obj);
|
||||||
free(vname);
|
free(vname);
|
||||||
}
|
}
|
||||||
|
if (cb->cb_json) {
|
||||||
|
if (!nvlist_empty(obj))
|
||||||
|
fnvlist_add_nvlist(item, class_name[n], obj);
|
||||||
|
fnvlist_free(obj);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_L2CACHE,
|
if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_L2CACHE,
|
||||||
&child, &children) == 0 && children > 0) {
|
&child, &children) == 0 && children > 0) {
|
||||||
|
if (cb->cb_json) {
|
||||||
|
l2c = fnvlist_alloc();
|
||||||
|
} else {
|
||||||
/* LINTED E_SEC_PRINTF_VAR_FMT */
|
/* LINTED E_SEC_PRINTF_VAR_FMT */
|
||||||
(void) printf(dashes, cb->cb_namewidth, "cache");
|
(void) printf(dashes, cb->cb_namewidth, "cache");
|
||||||
|
}
|
||||||
for (c = 0; c < children; c++) {
|
for (c = 0; c < children; c++) {
|
||||||
vname = zpool_vdev_name(g_zfs, zhp, child[c],
|
vname = zpool_vdev_name(g_zfs, zhp, child[c],
|
||||||
cb->cb_name_flags);
|
cb->cb_name_flags);
|
||||||
print_list_stats(zhp, vname, child[c], cb, depth + 2,
|
collect_list_stats(zhp, vname, child[c], cb, depth + 2,
|
||||||
B_FALSE);
|
B_FALSE, l2c);
|
||||||
free(vname);
|
free(vname);
|
||||||
}
|
}
|
||||||
|
if (cb->cb_json) {
|
||||||
|
if (!nvlist_empty(l2c))
|
||||||
|
fnvlist_add_nvlist(item, "l2cache", l2c);
|
||||||
|
fnvlist_free(l2c);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_SPARES, &child,
|
if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_SPARES, &child,
|
||||||
&children) == 0 && children > 0) {
|
&children) == 0 && children > 0) {
|
||||||
|
if (cb->cb_json) {
|
||||||
|
sp = fnvlist_alloc();
|
||||||
|
} else {
|
||||||
/* LINTED E_SEC_PRINTF_VAR_FMT */
|
/* LINTED E_SEC_PRINTF_VAR_FMT */
|
||||||
(void) printf(dashes, cb->cb_namewidth, "spare");
|
(void) printf(dashes, cb->cb_namewidth, "spare");
|
||||||
|
}
|
||||||
for (c = 0; c < children; c++) {
|
for (c = 0; c < children; c++) {
|
||||||
vname = zpool_vdev_name(g_zfs, zhp, child[c],
|
vname = zpool_vdev_name(g_zfs, zhp, child[c],
|
||||||
cb->cb_name_flags);
|
cb->cb_name_flags);
|
||||||
print_list_stats(zhp, vname, child[c], cb, depth + 2,
|
collect_list_stats(zhp, vname, child[c], cb, depth + 2,
|
||||||
B_TRUE);
|
B_TRUE, sp);
|
||||||
free(vname);
|
free(vname);
|
||||||
}
|
}
|
||||||
|
if (cb->cb_json) {
|
||||||
|
if (!nvlist_empty(sp))
|
||||||
|
fnvlist_add_nvlist(item, "spares", sp);
|
||||||
|
fnvlist_free(sp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name != NULL && cb->cb_json) {
|
||||||
|
fnvlist_add_nvlist(item, name, ent);
|
||||||
|
fnvlist_free(ent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -6849,17 +6977,44 @@ print_list_stats(zpool_handle_t *zhp, const char *name, nvlist_t *nv,
|
|||||||
static int
|
static int
|
||||||
list_callback(zpool_handle_t *zhp, void *data)
|
list_callback(zpool_handle_t *zhp, void *data)
|
||||||
{
|
{
|
||||||
|
nvlist_t *p, *d, *nvdevs;
|
||||||
|
uint64_t guid;
|
||||||
|
char pool_guid[256];
|
||||||
|
const char *pool_name = zpool_get_name(zhp);
|
||||||
list_cbdata_t *cbp = data;
|
list_cbdata_t *cbp = data;
|
||||||
|
p = d = nvdevs = NULL;
|
||||||
|
|
||||||
print_pool(zhp, cbp);
|
collect_pool(zhp, cbp);
|
||||||
|
|
||||||
if (cbp->cb_verbose) {
|
if (cbp->cb_verbose) {
|
||||||
nvlist_t *config, *nvroot;
|
nvlist_t *config, *nvroot;
|
||||||
|
|
||||||
config = zpool_get_config(zhp, NULL);
|
config = zpool_get_config(zhp, NULL);
|
||||||
verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
|
verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
|
||||||
&nvroot) == 0);
|
&nvroot) == 0);
|
||||||
print_list_stats(zhp, NULL, nvroot, cbp, 0, B_FALSE);
|
if (cbp->cb_json) {
|
||||||
|
d = fnvlist_lookup_nvlist(cbp->cb_jsobj,
|
||||||
|
"pools");
|
||||||
|
if (cbp->cb_json_pool_key_guid) {
|
||||||
|
guid = fnvlist_lookup_uint64(config,
|
||||||
|
ZPOOL_CONFIG_POOL_GUID);
|
||||||
|
snprintf(pool_guid, 256, "%llu",
|
||||||
|
(u_longlong_t)guid);
|
||||||
|
p = fnvlist_lookup_nvlist(d, pool_guid);
|
||||||
|
} else {
|
||||||
|
p = fnvlist_lookup_nvlist(d, pool_name);
|
||||||
|
}
|
||||||
|
nvdevs = fnvlist_alloc();
|
||||||
|
}
|
||||||
|
collect_list_stats(zhp, NULL, nvroot, cbp, 0, B_FALSE, nvdevs);
|
||||||
|
if (cbp->cb_json) {
|
||||||
|
fnvlist_add_nvlist(p, "vdevs", nvdevs);
|
||||||
|
if (cbp->cb_json_pool_key_guid)
|
||||||
|
fnvlist_add_nvlist(d, pool_guid, p);
|
||||||
|
else
|
||||||
|
fnvlist_add_nvlist(d, pool_name, p);
|
||||||
|
fnvlist_add_nvlist(cbp->cb_jsobj, "pools", d);
|
||||||
|
fnvlist_free(nvdevs);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (0);
|
return (0);
|
||||||
@ -6899,6 +7054,9 @@ get_namewidth_list(zpool_handle_t *zhp, void *data)
|
|||||||
* -p Display values in parsable (exact) format.
|
* -p Display values in parsable (exact) format.
|
||||||
* -P Display full path for vdev name.
|
* -P Display full path for vdev name.
|
||||||
* -T Display a timestamp in date(1) or Unix format
|
* -T Display a timestamp in date(1) or Unix format
|
||||||
|
* -j Display the output in JSON format
|
||||||
|
* --json-int Display the numbers as integer instead of strings.
|
||||||
|
* --json-pool-key-guid Set pool GUID as key for pool objects.
|
||||||
*
|
*
|
||||||
* List all pools in the system, whether or not they're healthy. Output space
|
* List all pools in the system, whether or not they're healthy. Output space
|
||||||
* statistics for each one, as well as health status summary.
|
* statistics for each one, as well as health status summary.
|
||||||
@ -6917,10 +7075,19 @@ zpool_do_list(int argc, char **argv)
|
|||||||
unsigned long count = 0;
|
unsigned long count = 0;
|
||||||
zpool_list_t *list;
|
zpool_list_t *list;
|
||||||
boolean_t first = B_TRUE;
|
boolean_t first = B_TRUE;
|
||||||
|
nvlist_t *data = NULL;
|
||||||
current_prop_type = ZFS_TYPE_POOL;
|
current_prop_type = ZFS_TYPE_POOL;
|
||||||
|
|
||||||
|
struct option long_options[] = {
|
||||||
|
{"json-int", no_argument, NULL, ZPOOL_OPTION_JSON_NUMS_AS_INT},
|
||||||
|
{"json-pool-key-guid", no_argument, NULL,
|
||||||
|
ZPOOL_OPTION_POOL_KEY_GUID},
|
||||||
|
{0, 0, 0, 0}
|
||||||
|
};
|
||||||
|
|
||||||
/* check options */
|
/* check options */
|
||||||
while ((c = getopt(argc, argv, ":gHLo:pPT:v")) != -1) {
|
while ((c = getopt_long(argc, argv, ":gjHLo:pPT:v", long_options,
|
||||||
|
NULL)) != -1) {
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'g':
|
case 'g':
|
||||||
cb.cb_name_flags |= VDEV_NAME_GUID;
|
cb.cb_name_flags |= VDEV_NAME_GUID;
|
||||||
@ -6940,6 +7107,16 @@ zpool_do_list(int argc, char **argv)
|
|||||||
case 'p':
|
case 'p':
|
||||||
cb.cb_literal = B_TRUE;
|
cb.cb_literal = B_TRUE;
|
||||||
break;
|
break;
|
||||||
|
case 'j':
|
||||||
|
cb.cb_json = B_TRUE;
|
||||||
|
break;
|
||||||
|
case ZPOOL_OPTION_JSON_NUMS_AS_INT:
|
||||||
|
cb.cb_json_as_int = B_TRUE;
|
||||||
|
cb.cb_literal = B_TRUE;
|
||||||
|
break;
|
||||||
|
case ZPOOL_OPTION_POOL_KEY_GUID:
|
||||||
|
cb.cb_json_pool_key_guid = B_TRUE;
|
||||||
|
break;
|
||||||
case 'T':
|
case 'T':
|
||||||
get_timestamp_arg(*optarg);
|
get_timestamp_arg(*optarg);
|
||||||
break;
|
break;
|
||||||
@ -6962,6 +7139,18 @@ zpool_do_list(int argc, char **argv)
|
|||||||
argc -= optind;
|
argc -= optind;
|
||||||
argv += optind;
|
argv += optind;
|
||||||
|
|
||||||
|
if (!cb.cb_json && cb.cb_json_as_int) {
|
||||||
|
(void) fprintf(stderr, gettext("'--json-int' only works with"
|
||||||
|
" '-j' option\n"));
|
||||||
|
usage(B_FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cb.cb_json && cb.cb_json_pool_key_guid) {
|
||||||
|
(void) fprintf(stderr, gettext("'json-pool-key-guid' only"
|
||||||
|
" works with '-j' option\n"));
|
||||||
|
usage(B_FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
get_interval_count(&argc, argv, &interval, &count);
|
get_interval_count(&argc, argv, &interval, &count);
|
||||||
|
|
||||||
if (zprop_get_list(g_zfs, props, &cb.cb_proplist, ZFS_TYPE_POOL) != 0)
|
if (zprop_get_list(g_zfs, props, &cb.cb_proplist, ZFS_TYPE_POOL) != 0)
|
||||||
@ -6975,18 +7164,43 @@ zpool_do_list(int argc, char **argv)
|
|||||||
if (pool_list_count(list) == 0)
|
if (pool_list_count(list) == 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
if (cb.cb_json) {
|
||||||
|
cb.cb_jsobj = zpool_json_schema(0, 1);
|
||||||
|
data = fnvlist_alloc();
|
||||||
|
fnvlist_add_nvlist(cb.cb_jsobj, "pools", data);
|
||||||
|
fnvlist_free(data);
|
||||||
|
}
|
||||||
|
|
||||||
cb.cb_namewidth = 0;
|
cb.cb_namewidth = 0;
|
||||||
(void) pool_list_iter(list, B_FALSE, get_namewidth_list, &cb);
|
(void) pool_list_iter(list, B_FALSE, get_namewidth_list, &cb);
|
||||||
|
|
||||||
if (timestamp_fmt != NODATE)
|
if (timestamp_fmt != NODATE) {
|
||||||
|
if (cb.cb_json) {
|
||||||
|
if (cb.cb_json_as_int) {
|
||||||
|
fnvlist_add_uint64(cb.cb_jsobj, "time",
|
||||||
|
time(NULL));
|
||||||
|
} else {
|
||||||
|
char ts[128];
|
||||||
|
get_timestamp(timestamp_fmt, ts, 128);
|
||||||
|
fnvlist_add_string(cb.cb_jsobj, "time",
|
||||||
|
ts);
|
||||||
|
}
|
||||||
|
} else
|
||||||
print_timestamp(timestamp_fmt);
|
print_timestamp(timestamp_fmt);
|
||||||
|
}
|
||||||
|
|
||||||
if (!cb.cb_scripted && (first || cb.cb_verbose)) {
|
if (!cb.cb_scripted && (first || cb.cb_verbose) &&
|
||||||
|
!cb.cb_json) {
|
||||||
print_header(&cb);
|
print_header(&cb);
|
||||||
first = B_FALSE;
|
first = B_FALSE;
|
||||||
}
|
}
|
||||||
ret = pool_list_iter(list, B_TRUE, list_callback, &cb);
|
ret = pool_list_iter(list, B_TRUE, list_callback, &cb);
|
||||||
|
|
||||||
|
if (ret == 0 && cb.cb_json)
|
||||||
|
zcmd_print_json(cb.cb_jsobj);
|
||||||
|
else if (ret != 0 && cb.cb_json)
|
||||||
|
nvlist_free(cb.cb_jsobj);
|
||||||
|
|
||||||
if (interval == 0)
|
if (interval == 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -6999,7 +7213,8 @@ zpool_do_list(int argc, char **argv)
|
|||||||
(void) fsleep(interval);
|
(void) fsleep(interval);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argc == 0 && !cb.cb_scripted && pool_list_count(list) == 0) {
|
if (argc == 0 && !cb.cb_scripted && !cb.cb_json &&
|
||||||
|
pool_list_count(list) == 0) {
|
||||||
(void) printf(gettext("no pools available\n"));
|
(void) printf(gettext("no pools available\n"));
|
||||||
ret = 0;
|
ret = 0;
|
||||||
}
|
}
|
||||||
@ -10886,7 +11101,7 @@ get_callback_vdev(zpool_handle_t *zhp, char *vdevname, void *data)
|
|||||||
if (cbp->cb_json) {
|
if (cbp->cb_json) {
|
||||||
if (!nvlist_empty(props)) {
|
if (!nvlist_empty(props)) {
|
||||||
item = fnvlist_alloc();
|
item = fnvlist_alloc();
|
||||||
fill_vdev_info(item, zhp, vdevname,
|
fill_vdev_info(item, zhp, vdevname, B_TRUE,
|
||||||
cbp->cb_json_as_int);
|
cbp->cb_json_as_int);
|
||||||
fnvlist_add_nvlist(item, "properties", props);
|
fnvlist_add_nvlist(item, "properties", props);
|
||||||
fnvlist_add_nvlist(d, vdevname, item);
|
fnvlist_add_nvlist(d, vdevname, item);
|
||||||
|
@ -37,5 +37,7 @@
|
|||||||
|
|
||||||
/* Print a timestamp in either Unix or standard format. */
|
/* Print a timestamp in either Unix or standard format. */
|
||||||
void print_timestamp(uint_t);
|
void print_timestamp(uint_t);
|
||||||
|
/* Return timestamp in either Unix or standard format in provided buffer */
|
||||||
|
void get_timestamp(uint_t, char *, int);
|
||||||
|
|
||||||
#endif /* _STATCOMMON_H */
|
#endif /* _STATCOMMON_H */
|
||||||
|
@ -62,3 +62,25 @@ print_timestamp(uint_t timestamp_fmt)
|
|||||||
(void) printf("%s\n", dstr);
|
(void) printf("%s\n", dstr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return timestamp as decimal reprentation (in string) of time_t
|
||||||
|
* value (-T u was specified) or in date(1) format (-T d was specified).
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
get_timestamp(uint_t timestamp_fmt, char *buf, int len)
|
||||||
|
{
|
||||||
|
time_t t = time(NULL);
|
||||||
|
static const char *fmt = NULL;
|
||||||
|
|
||||||
|
/* We only need to retrieve this once per invocation */
|
||||||
|
if (fmt == NULL)
|
||||||
|
fmt = nl_langinfo(_DATE_FMT);
|
||||||
|
|
||||||
|
if (timestamp_fmt == UDATE) {
|
||||||
|
(void) snprintf(buf, len, "%lld", (longlong_t)t);
|
||||||
|
} else if (timestamp_fmt == DDATE) {
|
||||||
|
struct tm tm;
|
||||||
|
strftime(buf, len, fmt, localtime_r(&t, &tm));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -37,6 +37,7 @@
|
|||||||
.Nm zpool
|
.Nm zpool
|
||||||
.Cm list
|
.Cm list
|
||||||
.Op Fl HgLpPv
|
.Op Fl HgLpPv
|
||||||
|
.Op Fl j Op Ar --json-int, --json-pool-key-guid
|
||||||
.Op Fl o Ar property Ns Oo , Ns Ar property Oc Ns …
|
.Op Fl o Ar property Ns Oo , Ns Ar property Oc Ns …
|
||||||
.Op Fl T Sy u Ns | Ns Sy d
|
.Op Fl T Sy u Ns | Ns Sy d
|
||||||
.Oo Ar pool Oc Ns …
|
.Oo Ar pool Oc Ns …
|
||||||
@ -58,6 +59,14 @@ is specified, the command exits after
|
|||||||
.Ar count
|
.Ar count
|
||||||
reports are printed.
|
reports are printed.
|
||||||
.Bl -tag -width Ds
|
.Bl -tag -width Ds
|
||||||
|
.It Fl j Op Ar --json-int, --json-pool-key-guid
|
||||||
|
Display the list of pools in JSON format.
|
||||||
|
Specify
|
||||||
|
.Sy --json-int
|
||||||
|
to display the numbers in integer format instead of strings.
|
||||||
|
Specify
|
||||||
|
.Sy --json-pool-key-guid
|
||||||
|
to set pool GUID as key for pool objects instead of pool names.
|
||||||
.It Fl g
|
.It Fl g
|
||||||
Display vdev GUIDs instead of the normal device names.
|
Display vdev GUIDs instead of the normal device names.
|
||||||
These GUIDs can be used in place of device names for the zpool
|
These GUIDs can be used in place of device names for the zpool
|
||||||
@ -139,6 +148,104 @@ data 23.9G 14.6G 9.30G - 48% 61% 1.00x ONLINE -
|
|||||||
sda - - - - -
|
sda - - - - -
|
||||||
sdb - - - 10G -
|
sdb - - - 10G -
|
||||||
sdc - - - - -
|
sdc - - - - -
|
||||||
|
.Ed
|
||||||
|
.
|
||||||
|
.Ss Example 3 : No Displaying expanded space on a device
|
||||||
|
The following command lists all available pools on the system in JSON
|
||||||
|
format.
|
||||||
|
.Bd -literal -compact -offset Ds
|
||||||
|
.No # Nm zpool Cm list Fl j | Nm jq
|
||||||
|
{
|
||||||
|
"output_version": {
|
||||||
|
"command": "zpool list",
|
||||||
|
"vers_major": 0,
|
||||||
|
"vers_minor": 1
|
||||||
|
},
|
||||||
|
"pools": {
|
||||||
|
"tank": {
|
||||||
|
"name": "tank",
|
||||||
|
"type": "POOL",
|
||||||
|
"state": "ONLINE",
|
||||||
|
"guid": "15220353080205405147",
|
||||||
|
"txg": "2671",
|
||||||
|
"spa_version": "5000",
|
||||||
|
"zpl_version": "5",
|
||||||
|
"properties": {
|
||||||
|
"size": {
|
||||||
|
"value": "111G",
|
||||||
|
"source": {
|
||||||
|
"type": "NONE",
|
||||||
|
"data": "-"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"allocated": {
|
||||||
|
"value": "30.8G",
|
||||||
|
"source": {
|
||||||
|
"type": "NONE",
|
||||||
|
"data": "-"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"free": {
|
||||||
|
"value": "80.2G",
|
||||||
|
"source": {
|
||||||
|
"type": "NONE",
|
||||||
|
"data": "-"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"checkpoint": {
|
||||||
|
"value": "-",
|
||||||
|
"source": {
|
||||||
|
"type": "NONE",
|
||||||
|
"data": "-"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"expandsize": {
|
||||||
|
"value": "-",
|
||||||
|
"source": {
|
||||||
|
"type": "NONE",
|
||||||
|
"data": "-"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fragmentation": {
|
||||||
|
"value": "0%",
|
||||||
|
"source": {
|
||||||
|
"type": "NONE",
|
||||||
|
"data": "-"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"capacity": {
|
||||||
|
"value": "27%",
|
||||||
|
"source": {
|
||||||
|
"type": "NONE",
|
||||||
|
"data": "-"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dedupratio": {
|
||||||
|
"value": "1.00x",
|
||||||
|
"source": {
|
||||||
|
"type": "NONE",
|
||||||
|
"data": "-"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"health": {
|
||||||
|
"value": "ONLINE",
|
||||||
|
"source": {
|
||||||
|
"type": "NONE",
|
||||||
|
"data": "-"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"altroot": {
|
||||||
|
"value": "-",
|
||||||
|
"source": {
|
||||||
|
"type": "DEFAULT",
|
||||||
|
"data": "-"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.Ed
|
.Ed
|
||||||
.
|
.
|
||||||
.Sh SEE ALSO
|
.Sh SEE ALSO
|
||||||
|
Loading…
Reference in New Issue
Block a user