Simplify diagnostic kstat for pool status
This commit updates the kstat for pool status and simplifies by creating an nvlist that contains the pool status. This nvlist is then printed to provided buffer in JSON format. The redundant parts of code have also been removed. Signed-off-by: Umer Saleem <usaleem@ixsystems.com>
This commit is contained in:
parent
420a125bdc
commit
138612c989
|
@ -175,7 +175,6 @@ COMMON_H = \
|
|||
|
||||
|
||||
KERNEL_H = \
|
||||
sys/jprint.h \
|
||||
sys/spa_json_stats.h \
|
||||
sys/zfs_ioctl.h \
|
||||
sys/zfs_ioctl_impl.h \
|
||||
|
|
|
@ -1,70 +0,0 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://opensource.org/licenses/CDDL-1.0.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2024, Klara Inc.
|
||||
*/
|
||||
|
||||
#ifndef _SYS_JPRINT_H
|
||||
#define _SYS_JPRINT_H
|
||||
|
||||
/* maximum stack nesting */
|
||||
#define JP_MAX_STACK 32
|
||||
|
||||
enum jp_type {
|
||||
JP_OBJECT = 1,
|
||||
JP_ARRAY
|
||||
};
|
||||
|
||||
struct jp_stack {
|
||||
enum jp_type type;
|
||||
int nelem;
|
||||
};
|
||||
|
||||
typedef struct jprint {
|
||||
char *buffer; /* pointer to application's buffer */
|
||||
size_t buflen; /* length of buffer */
|
||||
char *bufp; /* current write position in buffer */
|
||||
char tmpbuf[32]; /* local buffer for conversions */
|
||||
int error; /* error code */
|
||||
int ncall; /* API call number on which error occurred */
|
||||
struct jp_stack /* stack of array/object nodes */
|
||||
stack[JP_MAX_STACK];
|
||||
int stackp;
|
||||
} jprint_t;
|
||||
|
||||
/* error return codes */
|
||||
#define JPRINT_OK 0 /* no error */
|
||||
#define JPRINT_BUF_FULL 1 /* output buffer full */
|
||||
#define JPRINT_NEST_ERROR 2 /* nesting error */
|
||||
#define JPRINT_STACK_FULL 3 /* array/object nesting */
|
||||
#define JPRINT_STACK_EMPTY 4 /* stack underflow error */
|
||||
#define JPRINT_OPEN 5 /* not all objects closed */
|
||||
#define JPRINT_FMT 6 /* format error */
|
||||
|
||||
const char *jp_errorstring(int err);
|
||||
int jp_error(jprint_t *jp);
|
||||
void jp_open(jprint_t *jp, char *buffer, size_t buflen);
|
||||
int jp_close(jprint_t *jp);
|
||||
int jp_errorpos(jprint_t *jp);
|
||||
int jp_printf(jprint_t *jp, const char *fmt, ...);
|
||||
|
||||
#endif /* _SYS_JPRINT_H */
|
|
@ -213,6 +213,8 @@ _SYS_NVPAIR_H int nvlist_remove(nvlist_t *, const char *, data_type_t);
|
|||
_SYS_NVPAIR_H int nvlist_remove_all(nvlist_t *, const char *);
|
||||
_SYS_NVPAIR_H int nvlist_remove_nvpair(nvlist_t *, nvpair_t *);
|
||||
|
||||
_SYS_NVPAIR_H int nvlist_to_json(nvlist_t *, char **, size_t);
|
||||
|
||||
_SYS_NVPAIR_H int nvlist_lookup_boolean(const nvlist_t *, const char *);
|
||||
_SYS_NVPAIR_H int nvlist_lookup_boolean_value(const nvlist_t *, const char *,
|
||||
boolean_t *);
|
||||
|
|
|
@ -117,6 +117,7 @@
|
|||
<elf-symbol name='nvlist_empty' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
|
||||
<elf-symbol name='nvlist_exists' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
|
||||
<elf-symbol name='nvlist_free' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
|
||||
<elf-symbol name='nvlist_to_json' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
|
||||
<elf-symbol name='nvlist_lookup_boolean' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
|
||||
<elf-symbol name='nvlist_lookup_boolean_array' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
|
||||
<elf-symbol name='nvlist_lookup_boolean_value' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
|
||||
|
@ -2780,6 +2781,12 @@
|
|||
<parameter type-id='3fa542f0' name='nvp'/>
|
||||
<return type-id='95e97e5e'/>
|
||||
</function-decl>
|
||||
<function-decl name='nvlist_to_json' mangled-name='nvlist_to_json' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='nvlist_to_json'>
|
||||
<parameter type-id='5ce45b60' name='nvl'/>
|
||||
<parameter type-id='9b23c9ad' name='buf'/>
|
||||
<parameter type-id='b59d7dce' name='size'/>
|
||||
<return type-id='95e97e5e'/>
|
||||
</function-decl>
|
||||
<function-decl name='nvlist_add_boolean' mangled-name='nvlist_add_boolean' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='nvlist_add_boolean'>
|
||||
<parameter type-id='5ce45b60' name='nvl'/>
|
||||
<parameter type-id='80f4b756' name='name'/>
|
||||
|
|
|
@ -112,7 +112,6 @@ nodist_libzpool_la_SOURCES = \
|
|||
module/zfs/fm.c \
|
||||
module/zfs/gzip.c \
|
||||
module/zfs/hkdf.c \
|
||||
module/zfs/jprint.c \
|
||||
module/zfs/lz4.c \
|
||||
module/zfs/lz4_zfs.c \
|
||||
module/zfs/lzjb.c \
|
||||
|
|
|
@ -353,7 +353,6 @@ ZFS_OBJS := \
|
|||
fm.o \
|
||||
gzip.o \
|
||||
hkdf.o \
|
||||
jprint.o \
|
||||
lz4.o \
|
||||
lz4_zfs.o \
|
||||
lzjb.o \
|
||||
|
|
|
@ -282,7 +282,6 @@ SRCS+= abd.c \
|
|||
edonr_zfs.c \
|
||||
fm.c \
|
||||
gzip.c \
|
||||
jprint.c \
|
||||
lz4.c \
|
||||
lz4_zfs.c \
|
||||
lzjb.c \
|
||||
|
|
|
@ -994,6 +994,369 @@ nvlist_remove_nvpair(nvlist_t *nvl, nvpair_t *nvp)
|
|||
return (0);
|
||||
}
|
||||
|
||||
#define JPRINTF(start, end, ...) \
|
||||
do { \
|
||||
if (start < end) { \
|
||||
int ret = snprintf(start, end - start, __VA_ARGS__); \
|
||||
if (ret < 0) \
|
||||
return (ENOMEM); \
|
||||
start += ret; \
|
||||
} else \
|
||||
return (ENOMEM); \
|
||||
} while (0)
|
||||
|
||||
static int
|
||||
nvlist_json_string(const char *s, char **buf, size_t size)
|
||||
{
|
||||
char *p = *buf;
|
||||
char *end = *buf + size;
|
||||
static const char *hex = "0123456789ABCDEF";
|
||||
int c;
|
||||
|
||||
if (s == NULL) {
|
||||
JPRINTF(p, end, "null");
|
||||
*buf = p;
|
||||
return (0);
|
||||
}
|
||||
|
||||
JPRINTF(p, end, "\"");
|
||||
while (*s) {
|
||||
c = (int)*s++;
|
||||
/* formfeed, newline, return, tab, backspace */
|
||||
if (c == 12)
|
||||
JPRINTF(p, end, "\\f");
|
||||
else if (c == 10)
|
||||
JPRINTF(p, end, "\\n");
|
||||
else if (c == 13)
|
||||
JPRINTF(p, end, "\\r");
|
||||
else if (c == 9)
|
||||
JPRINTF(p, end, "\\t");
|
||||
else if (c == 8)
|
||||
JPRINTF(p, end, "\\b");
|
||||
/*
|
||||
* all characters from 0x00 to 0x1f, and 0x7f are
|
||||
* escaped as: \u00xx
|
||||
*/
|
||||
else if (((0 <= c) && (c <= 0x1f)) || (c == 0x7f)) {
|
||||
JPRINTF(p, end, "\\u00%c%c",
|
||||
hex[(c >> 4) & 0x0f], hex[c & 0x0f]);
|
||||
} else if (c == '"')
|
||||
JPRINTF(p, end, "\\\"");
|
||||
else if (c == '\\')
|
||||
JPRINTF(p, end, "\\\\");
|
||||
else if (c == '/')
|
||||
JPRINTF(p, end, "\\/");
|
||||
/*
|
||||
* all other printable characters ' ' to '~', and
|
||||
* any utf-8 sequences (high bit set):
|
||||
* 1xxxxxxx 10xxxxxx ...
|
||||
* is a utf-8 sequence (10xxxxxx may occur 1 to 3 times).
|
||||
* Note that this is simply distinguished here as high
|
||||
* bit set.
|
||||
*/
|
||||
else
|
||||
JPRINTF(p, end, "%c", c);
|
||||
}
|
||||
JPRINTF(p, end, "\"");
|
||||
*buf = p;
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
nvlist_to_json(nvlist_t *nvl, char **buf, size_t size)
|
||||
{
|
||||
boolean_t first = B_TRUE;
|
||||
char *p = *buf;
|
||||
char *end = *buf + size;
|
||||
nvpair_t *curr = nvlist_next_nvpair(nvl, NULL);
|
||||
|
||||
JPRINTF(p, end, "{");
|
||||
|
||||
while (curr) {
|
||||
if (!first)
|
||||
JPRINTF(p, end, ",");
|
||||
else
|
||||
first = B_FALSE;
|
||||
|
||||
if (nvlist_json_string(nvpair_name(curr), &p, end - p) != 0)
|
||||
return (ENOMEM);
|
||||
JPRINTF(p, end, ":");
|
||||
|
||||
switch (nvpair_type(curr)) {
|
||||
case DATA_TYPE_STRING: {
|
||||
if (nvlist_json_string(fnvpair_value_string(curr), &p,
|
||||
end - p) != 0)
|
||||
return (ENOMEM);
|
||||
break;
|
||||
}
|
||||
|
||||
case DATA_TYPE_BOOLEAN: {
|
||||
JPRINTF(p, end, "true");
|
||||
break;
|
||||
}
|
||||
|
||||
case DATA_TYPE_BOOLEAN_VALUE: {
|
||||
JPRINTF(p, end, "%s",
|
||||
fnvpair_value_boolean_value(curr) == B_TRUE ?
|
||||
"true" : "false");
|
||||
break;
|
||||
}
|
||||
|
||||
case DATA_TYPE_BYTE: {
|
||||
JPRINTF(p, end, "%hhu", fnvpair_value_byte(curr));
|
||||
break;
|
||||
}
|
||||
|
||||
case DATA_TYPE_INT8: {
|
||||
JPRINTF(p, end, "%hhd", fnvpair_value_int8(curr));
|
||||
break;
|
||||
}
|
||||
|
||||
case DATA_TYPE_UINT8: {
|
||||
JPRINTF(p, end, "%hhu", fnvpair_value_uint8(curr));
|
||||
break;
|
||||
}
|
||||
|
||||
case DATA_TYPE_INT16: {
|
||||
JPRINTF(p, end, "%hd", fnvpair_value_int16(curr));
|
||||
break;
|
||||
}
|
||||
|
||||
case DATA_TYPE_UINT16: {
|
||||
JPRINTF(p, end, "%hu", fnvpair_value_uint16(curr));
|
||||
break;
|
||||
}
|
||||
|
||||
case DATA_TYPE_INT32: {
|
||||
JPRINTF(p, end, "%d", fnvpair_value_int32(curr));
|
||||
break;
|
||||
}
|
||||
|
||||
case DATA_TYPE_UINT32: {
|
||||
JPRINTF(p, end, "%u", fnvpair_value_uint32(curr));
|
||||
break;
|
||||
}
|
||||
|
||||
case DATA_TYPE_INT64: {
|
||||
JPRINTF(p, end, "%lld",
|
||||
(long long)fnvpair_value_int64(curr));
|
||||
break;
|
||||
}
|
||||
|
||||
case DATA_TYPE_UINT64: {
|
||||
JPRINTF(p, end, "%llu",
|
||||
(unsigned long long)fnvpair_value_uint64(curr));
|
||||
break;
|
||||
}
|
||||
|
||||
case DATA_TYPE_HRTIME: {
|
||||
hrtime_t val;
|
||||
VERIFY0(nvpair_value_hrtime(curr, &val));
|
||||
JPRINTF(p, end, "%llu", (unsigned long long)val);
|
||||
break;
|
||||
}
|
||||
|
||||
#if !defined(_KERNEL)
|
||||
case DATA_TYPE_DOUBLE: {
|
||||
double val;
|
||||
VERIFY0(nvpair_value_double(curr, &val));
|
||||
JPRINTF(p, end, "%f", val);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
case DATA_TYPE_NVLIST: {
|
||||
if (nvlist_to_json(fnvpair_value_nvlist(curr), &p,
|
||||
end - p) != 0)
|
||||
return (ENOMEM);
|
||||
break;
|
||||
}
|
||||
|
||||
case DATA_TYPE_STRING_ARRAY: {
|
||||
const char **val;
|
||||
uint_t valsz, i;
|
||||
VERIFY0(nvpair_value_string_array(curr, &val, &valsz));
|
||||
JPRINTF(p, end, "[");
|
||||
for (i = 0; i < valsz; i++) {
|
||||
if (i > 0)
|
||||
JPRINTF(p, end, ",");
|
||||
if (nvlist_json_string(val[i], &p,
|
||||
end - p) != 0)
|
||||
return (ENOMEM);
|
||||
}
|
||||
JPRINTF(p, end, "]");
|
||||
break;
|
||||
}
|
||||
|
||||
case DATA_TYPE_NVLIST_ARRAY: {
|
||||
nvlist_t **val;
|
||||
uint_t valsz, i;
|
||||
VERIFY0(nvpair_value_nvlist_array(curr, &val, &valsz));
|
||||
JPRINTF(p, end, "[");
|
||||
for (i = 0; i < valsz; i++) {
|
||||
if (i > 0)
|
||||
JPRINTF(p, end, ",");
|
||||
if (nvlist_to_json(val[i], &p, end - p) != 0)
|
||||
return (ENOMEM);
|
||||
}
|
||||
JPRINTF(p, end, "]");
|
||||
break;
|
||||
}
|
||||
|
||||
case DATA_TYPE_BOOLEAN_ARRAY: {
|
||||
boolean_t *val;
|
||||
uint_t valsz, i;
|
||||
VERIFY0(nvpair_value_boolean_array(curr, &val, &valsz));
|
||||
JPRINTF(p, end, "[");
|
||||
for (i = 0; i < valsz; i++) {
|
||||
if (i > 0)
|
||||
JPRINTF(p, end, ",");
|
||||
JPRINTF(p, end, val[i] == B_TRUE ?
|
||||
"true" : "false");
|
||||
}
|
||||
JPRINTF(p, end, "]");
|
||||
break;
|
||||
}
|
||||
|
||||
case DATA_TYPE_BYTE_ARRAY: {
|
||||
uchar_t *val;
|
||||
uint_t valsz, i;
|
||||
VERIFY0(nvpair_value_byte_array(curr, &val, &valsz));
|
||||
JPRINTF(p, end, "[");
|
||||
for (i = 0; i < valsz; i++) {
|
||||
if (i > 0)
|
||||
JPRINTF(p, end, ",");
|
||||
JPRINTF(p, end, "%hhu", val[i]);
|
||||
}
|
||||
JPRINTF(p, end, "]");
|
||||
break;
|
||||
}
|
||||
|
||||
case DATA_TYPE_UINT8_ARRAY: {
|
||||
uint8_t *val;
|
||||
uint_t valsz, i;
|
||||
VERIFY0(nvpair_value_uint8_array(curr, &val, &valsz));
|
||||
JPRINTF(p, end, "[");
|
||||
for (i = 0; i < valsz; i++) {
|
||||
if (i > 0)
|
||||
JPRINTF(p, end, ",");
|
||||
JPRINTF(p, end, "%hhu", val[i]);
|
||||
}
|
||||
JPRINTF(p, end, "]");
|
||||
break;
|
||||
}
|
||||
|
||||
case DATA_TYPE_INT8_ARRAY: {
|
||||
int8_t *val;
|
||||
uint_t valsz, i;
|
||||
VERIFY0(nvpair_value_int8_array(curr, &val, &valsz));
|
||||
JPRINTF(p, end, "[");
|
||||
for (i = 0; i < valsz; i++) {
|
||||
if (i > 0)
|
||||
JPRINTF(p, end, ",");
|
||||
JPRINTF(p, end, "%hhd", val[i]);
|
||||
}
|
||||
JPRINTF(p, end, "]");
|
||||
break;
|
||||
}
|
||||
|
||||
case DATA_TYPE_UINT16_ARRAY: {
|
||||
uint16_t *val;
|
||||
uint_t valsz, i;
|
||||
VERIFY0(nvpair_value_uint16_array(curr, &val, &valsz));
|
||||
JPRINTF(p, end, "[");
|
||||
for (i = 0; i < valsz; i++) {
|
||||
if (i > 0)
|
||||
JPRINTF(p, end, ",");
|
||||
JPRINTF(p, end, "%hu", val[i]);
|
||||
}
|
||||
JPRINTF(p, end, "]");
|
||||
break;
|
||||
}
|
||||
|
||||
case DATA_TYPE_INT16_ARRAY: {
|
||||
int16_t *val;
|
||||
uint_t valsz, i;
|
||||
VERIFY0(nvpair_value_int16_array(curr, &val, &valsz));
|
||||
JPRINTF(p, end, "[");
|
||||
for (i = 0; i < valsz; i++) {
|
||||
if (i > 0) {
|
||||
JPRINTF(p, end, ",");
|
||||
}
|
||||
JPRINTF(p, end, "%hd", val[i]);
|
||||
}
|
||||
JPRINTF(p, end, "]");
|
||||
break;
|
||||
}
|
||||
|
||||
case DATA_TYPE_UINT32_ARRAY: {
|
||||
uint32_t *val;
|
||||
uint_t valsz, i;
|
||||
VERIFY0(nvpair_value_uint32_array(curr, &val, &valsz));
|
||||
JPRINTF(p, end, "[");
|
||||
for (i = 0; i < valsz; i++) {
|
||||
if (i > 0)
|
||||
JPRINTF(p, end, ",");
|
||||
JPRINTF(p, end, "%u", val[i]);
|
||||
}
|
||||
JPRINTF(p, end, "]");
|
||||
break;
|
||||
}
|
||||
|
||||
case DATA_TYPE_INT32_ARRAY: {
|
||||
int32_t *val;
|
||||
uint_t valsz, i;
|
||||
VERIFY0(nvpair_value_int32_array(curr, &val, &valsz));
|
||||
JPRINTF(p, end, "[");
|
||||
for (i = 0; i < valsz; i++) {
|
||||
if (i > 0)
|
||||
JPRINTF(p, end, ",");
|
||||
JPRINTF(p, end, "%d", val[i]);
|
||||
}
|
||||
JPRINTF(p, end, "]");
|
||||
break;
|
||||
}
|
||||
|
||||
case DATA_TYPE_UINT64_ARRAY: {
|
||||
uint64_t *val;
|
||||
uint_t valsz, i;
|
||||
VERIFY0(nvpair_value_uint64_array(curr, &val, &valsz));
|
||||
JPRINTF(p, end, "[");
|
||||
for (i = 0; i < valsz; i++) {
|
||||
if (i > 0)
|
||||
JPRINTF(p, end, ",");
|
||||
JPRINTF(p, end, "%llu",
|
||||
(unsigned long long)val[i]);
|
||||
}
|
||||
JPRINTF(p, end, "]");
|
||||
break;
|
||||
}
|
||||
|
||||
case DATA_TYPE_INT64_ARRAY: {
|
||||
int64_t *val;
|
||||
uint_t valsz, i;
|
||||
VERIFY0(nvpair_value_int64_array(curr, &val, &valsz));
|
||||
JPRINTF(p, end, "[");
|
||||
for (i = 0; i < valsz; i++) {
|
||||
if (i > 0)
|
||||
JPRINTF(p, end, ",");
|
||||
JPRINTF(p, end, "%lld", (long long)val[i]);
|
||||
}
|
||||
JPRINTF(p, end, "]");
|
||||
break;
|
||||
}
|
||||
|
||||
case DATA_TYPE_UNKNOWN:
|
||||
case DATA_TYPE_DONTCARE:
|
||||
return (-1);
|
||||
}
|
||||
curr = nvlist_next_nvpair(nvl, curr);
|
||||
}
|
||||
JPRINTF(p, end, "}");
|
||||
*buf = p;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function calculates the size of an nvpair value.
|
||||
*
|
||||
|
|
|
@ -1,423 +0,0 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://opensource.org/licenses/CDDL-1.0.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2024, Klara Inc.
|
||||
*/
|
||||
|
||||
#include <sys/zfs_context.h>
|
||||
#include <sys/jprint.h>
|
||||
|
||||
/* Formats for int64_t and uint64_t */
|
||||
#ifndef PRId64
|
||||
#define PRId64 "lld" /* %D, int64_t */
|
||||
#define PRIu64 "llu" /* %U, uint64_t */
|
||||
#endif
|
||||
|
||||
/* literal key length maximum */
|
||||
#define KEYLEN 255
|
||||
|
||||
/* return error position (call number of jp_printf) */
|
||||
int
|
||||
jp_errorpos(jprint_t *jp)
|
||||
{
|
||||
return (jp->ncall);
|
||||
}
|
||||
|
||||
/* return string for error code */
|
||||
const char *
|
||||
jp_errorstring(int err)
|
||||
{
|
||||
switch (err) {
|
||||
case JPRINT_OK: return ("jprint ok");
|
||||
case JPRINT_BUF_FULL: return ("jprint buffer full");
|
||||
case JPRINT_NEST_ERROR: return ("jprint nest error");
|
||||
case JPRINT_STACK_FULL: return ("jprint stack full");
|
||||
case JPRINT_STACK_EMPTY: return ("jprint stack empty");
|
||||
case JPRINT_OPEN: return ("jprint open");
|
||||
case JPRINT_FMT: return ("jprint format");
|
||||
default: return ("jprint unknown error");
|
||||
}
|
||||
return ("jprint unknown error");
|
||||
}
|
||||
|
||||
/* return error from jprint_t */
|
||||
int
|
||||
jp_error(jprint_t *jp)
|
||||
{
|
||||
return (jp->error);
|
||||
}
|
||||
|
||||
/* open json using buffer of length buflen */
|
||||
void
|
||||
jp_open(jprint_t *jp, char *buffer, size_t buflen)
|
||||
{
|
||||
jp->buffer = jp->bufp = buffer;
|
||||
jp->buflen = buflen;
|
||||
jp->error = JPRINT_OK;
|
||||
jp->ncall = 0;
|
||||
jp->stackp = -1;
|
||||
*buffer = '\0';
|
||||
}
|
||||
|
||||
|
||||
/* close json (return out of memory error) */
|
||||
int
|
||||
jp_close(jprint_t *jp)
|
||||
{
|
||||
if (jp->error != JPRINT_OK)
|
||||
return (jp->error);
|
||||
if (jp->stackp != -1)
|
||||
jp->error = JPRINT_OPEN;
|
||||
return (jp->error);
|
||||
}
|
||||
|
||||
|
||||
/* put character to json */
|
||||
static void
|
||||
jp_putc(jprint_t *jp, char c)
|
||||
{
|
||||
if (jp->error == JPRINT_OK) {
|
||||
if ((jp->bufp - jp->buffer + 1) >= jp->buflen)
|
||||
jp->error = JPRINT_BUF_FULL;
|
||||
else {
|
||||
*jp->bufp++ = c;
|
||||
*jp->bufp = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* put string to json */
|
||||
static void
|
||||
jp_puts(jprint_t *jp, char *s)
|
||||
{
|
||||
while (*s && (jp->error == JPRINT_OK))
|
||||
jp_putc(jp, *s++);
|
||||
}
|
||||
|
||||
|
||||
/* put quoted string to json */
|
||||
static void
|
||||
jp_putsq(jprint_t *jp, char *s)
|
||||
{
|
||||
static const char *hex = "0123456789ABCDEF";
|
||||
int c;
|
||||
|
||||
if (s == NULL) {
|
||||
jp_puts(jp, (char *)"null");
|
||||
return;
|
||||
}
|
||||
jp_putc(jp, '\"');
|
||||
while (*s && (jp->error == JPRINT_OK)) {
|
||||
c = (int)*s++;
|
||||
/* formfeed, newline, return, tab, backspace */
|
||||
if (c == 12)
|
||||
jp_puts(jp, (char *)"\\f");
|
||||
else if (c == 10)
|
||||
jp_puts(jp, (char *)"\\n");
|
||||
else if (c == 13)
|
||||
jp_puts(jp, (char *)"\\r");
|
||||
else if (c == 9)
|
||||
jp_puts(jp, (char *)"\\t");
|
||||
else if (c == 8)
|
||||
jp_puts(jp, (char *)"\\b");
|
||||
/*
|
||||
* all characters from 0x00 to 0x1f, and 0x7f are
|
||||
* escaped as: \u00xx
|
||||
*/
|
||||
else if (((0 <= c) && (c <= 0x1f)) || (c == 0x7f)) {
|
||||
jp_puts(jp, (char *)"\\u00");
|
||||
jp_putc(jp, hex[(c >> 4) & 0x0f]);
|
||||
jp_putc(jp, hex[c & 0x0f]);
|
||||
/* * " \ / */
|
||||
} else if (c == '"')
|
||||
jp_puts(jp, (char *)"\\\"");
|
||||
else if (c == '\\')
|
||||
jp_puts(jp, (char *)"\\\\");
|
||||
else if (c == '/')
|
||||
jp_puts(jp, (char *)"\\/");
|
||||
/*
|
||||
* all other printable characters ' ' to '~', and
|
||||
* any utf-8 sequences (high bit set):
|
||||
* 1xxxxxxx 10xxxxxx ...
|
||||
* is a utf-8 sequence (10xxxxxx may occur 1 to 3 times).
|
||||
* Note that this is simply distinguished here as high
|
||||
* bit set.
|
||||
*/
|
||||
else
|
||||
jp_putc(jp, (char)c);
|
||||
}
|
||||
jp_putc(jp, '\"');
|
||||
}
|
||||
|
||||
|
||||
/* put out key if object open. error if nothing open */
|
||||
static int
|
||||
jp_key(jprint_t *jp, char *key)
|
||||
{
|
||||
if (jp->error != JPRINT_OK)
|
||||
goto err;
|
||||
/* at top level, no frame exists yet, no error */
|
||||
if (jp->stackp == -1)
|
||||
goto err;
|
||||
/* stackp has been "popped" too many times */
|
||||
if (jp->stackp < -1) {
|
||||
jp->error = JPRINT_STACK_EMPTY;
|
||||
goto err;
|
||||
}
|
||||
/* put comma separator in (both object and array) */
|
||||
if (++jp->stack[jp->stackp].nelem > 1)
|
||||
jp_putc(jp, ',');
|
||||
/* if its in an object, put out the key and separator */
|
||||
if (jp->stack[jp->stackp].type == JP_OBJECT) {
|
||||
jp_putsq(jp, key);
|
||||
jp_putc(jp, ':');
|
||||
}
|
||||
err:
|
||||
return (jp->error);
|
||||
}
|
||||
|
||||
|
||||
/* printf to json */
|
||||
int
|
||||
jp_printf(jprint_t *jp, const char *fmt, ...)
|
||||
{
|
||||
char key[KEYLEN + 1];
|
||||
int k, i;
|
||||
va_list ap;
|
||||
int n;
|
||||
unsigned int u;
|
||||
int64_t n64;
|
||||
uint64_t u64;
|
||||
boolean_t b;
|
||||
char *s;
|
||||
char *start = jp->bufp;
|
||||
|
||||
if (jp->error != JPRINT_OK)
|
||||
return (-1);
|
||||
++jp->ncall;
|
||||
va_start(ap, fmt);
|
||||
key[k = 0] = '\0';
|
||||
while (*fmt && (jp->error == JPRINT_OK)) {
|
||||
/*
|
||||
* If we need to debug jprint format,
|
||||
* zfs_dbgmsg("====> jprint char = %c\n", *fmt);
|
||||
*/
|
||||
switch (*fmt) {
|
||||
case '%':
|
||||
++fmt;
|
||||
switch (*fmt) {
|
||||
case 'k': /* next parameter is key */
|
||||
if (jp->stackp < 0) {
|
||||
jp->error = JPRINT_STACK_EMPTY;
|
||||
break;
|
||||
}
|
||||
s = va_arg(ap, char *);
|
||||
if (strlen(s) <= KEYLEN)
|
||||
strcpy(key, s);
|
||||
else
|
||||
jp->error = JPRINT_FMT;
|
||||
break;
|
||||
case 'd': /* next parameter is int */
|
||||
if (jp->stackp < 0) {
|
||||
jp->error = JPRINT_STACK_EMPTY;
|
||||
break;
|
||||
}
|
||||
n = va_arg(ap, int);
|
||||
i = snprintf(
|
||||
jp->tmpbuf, sizeof (jp->tmpbuf),
|
||||
"%d", n);
|
||||
if (jp_key(jp, key) == JPRINT_OK) {
|
||||
if ((i >= sizeof (jp->tmpbuf)) ||
|
||||
(i < 0))
|
||||
jp_puts(jp, (char *)"####");
|
||||
else
|
||||
jp_puts(jp, jp->tmpbuf);
|
||||
}
|
||||
key[k = 0] = '\0';
|
||||
break;
|
||||
case 'u': /* next parameter is unsigned int */
|
||||
if (jp->stackp < 0) {
|
||||
jp->error = JPRINT_STACK_EMPTY;
|
||||
break;
|
||||
}
|
||||
u = va_arg(ap, unsigned int);
|
||||
i = snprintf(
|
||||
jp->tmpbuf, sizeof (jp->tmpbuf),
|
||||
"%u", u);
|
||||
if (jp_key(jp, key) == JPRINT_OK) {
|
||||
if ((i >= sizeof (jp->tmpbuf)) ||
|
||||
(i < 0))
|
||||
jp_puts(jp, (char *)"####");
|
||||
else
|
||||
jp_puts(jp, jp->tmpbuf);
|
||||
}
|
||||
key[k = 0] = '\0';
|
||||
break;
|
||||
case 'U': /* next parameter is uint64_t */
|
||||
if (jp->stackp < 0) {
|
||||
jp->error = JPRINT_STACK_EMPTY;
|
||||
break;
|
||||
}
|
||||
u64 = va_arg(ap, uint64_t);
|
||||
i = snprintf(
|
||||
jp->tmpbuf, sizeof (jp->tmpbuf),
|
||||
"%" PRIu64, u64);
|
||||
if (jp_key(jp, key) == JPRINT_OK) {
|
||||
if ((i >= sizeof (jp->tmpbuf)) ||
|
||||
(i < 0))
|
||||
jp_puts(jp, (char *)"####");
|
||||
else
|
||||
jp_puts(jp, jp->tmpbuf);
|
||||
}
|
||||
key[k = 0] = '\0';
|
||||
break;
|
||||
case 'D': /* next parameter is int64_t */
|
||||
if (jp->stackp < 0) {
|
||||
jp->error = JPRINT_STACK_EMPTY;
|
||||
break;
|
||||
}
|
||||
n64 = va_arg(ap, int64_t);
|
||||
i = snprintf(
|
||||
jp->tmpbuf, sizeof (jp->tmpbuf),
|
||||
"%" PRId64, n64);
|
||||
if (jp_key(jp, key) == JPRINT_OK) {
|
||||
if ((i >= sizeof (jp->tmpbuf)) ||
|
||||
(i < 0))
|
||||
jp_puts(jp, (char *)"####");
|
||||
else
|
||||
jp_puts(jp, jp->tmpbuf);
|
||||
}
|
||||
key[k = 0] = '\0';
|
||||
break;
|
||||
case 's': /* next parameter is string */
|
||||
if (jp->stackp < 0) {
|
||||
jp->error = JPRINT_STACK_EMPTY;
|
||||
break;
|
||||
}
|
||||
s = va_arg(ap, char *);
|
||||
if (jp_key(jp, key) == JPRINT_OK)
|
||||
jp_putsq(jp, s);
|
||||
key[k = 0] = '\0';
|
||||
break;
|
||||
case 'b': /* next parameter is boolean */
|
||||
if (jp->stackp < 0) {
|
||||
jp->error = JPRINT_STACK_EMPTY;
|
||||
break;
|
||||
}
|
||||
if (jp_key(jp, key) == JPRINT_OK) {
|
||||
b = (boolean_t)va_arg(ap, int);
|
||||
s = b ?
|
||||
(char *)"true" :
|
||||
(char *)"false";
|
||||
jp_puts(jp, s);
|
||||
}
|
||||
key[k = 0] = '\0';
|
||||
break;
|
||||
case '%': /* literal % */
|
||||
if (k < KEYLEN) {
|
||||
key[k++] = '%';
|
||||
key[k] = '\0';
|
||||
} else
|
||||
jp->error = JPRINT_FMT;
|
||||
break;
|
||||
default:
|
||||
jp->error = JPRINT_FMT;
|
||||
}
|
||||
break;
|
||||
case '{': /* open object */
|
||||
if (jp->stackp >= (JP_MAX_STACK - 1))
|
||||
jp->error = JPRINT_STACK_FULL;
|
||||
else {
|
||||
(void) jp_key(jp, key);
|
||||
++jp->stackp;
|
||||
jp->stack[jp->stackp].type = JP_OBJECT;
|
||||
jp->stack[jp->stackp].nelem = 0;
|
||||
jp_putc(jp, '{');
|
||||
}
|
||||
break;
|
||||
case '}': /* close object */
|
||||
if (jp->stackp < 0)
|
||||
jp->error = JPRINT_STACK_EMPTY;
|
||||
else if (jp->stack[jp->stackp].type != JP_OBJECT)
|
||||
jp->error = JPRINT_NEST_ERROR;
|
||||
else {
|
||||
--jp->stackp;
|
||||
jp_putc(jp, '}');
|
||||
}
|
||||
break;
|
||||
case '[': /* open array */
|
||||
if (jp->stackp >= (JP_MAX_STACK - 1))
|
||||
jp->error = JPRINT_STACK_FULL;
|
||||
else {
|
||||
(void) jp_key(jp, key);
|
||||
++jp->stackp;
|
||||
jp->stack[jp->stackp].type = JP_ARRAY;
|
||||
jp->stack[jp->stackp].nelem = 0;
|
||||
jp_putc(jp, '[');
|
||||
}
|
||||
break;
|
||||
case ']': /* close array */
|
||||
if (jp->stackp < 0)
|
||||
jp->error = JPRINT_STACK_EMPTY;
|
||||
else if (jp->stack[jp->stackp].type != JP_ARRAY)
|
||||
jp->error = JPRINT_NEST_ERROR;
|
||||
else {
|
||||
--jp->stackp;
|
||||
jp_putc(jp, ']');
|
||||
}
|
||||
break;
|
||||
|
||||
case ',': /* ,: space tab are ignored */
|
||||
case ':':
|
||||
case ' ':
|
||||
case '\t':
|
||||
break;
|
||||
case '\\':
|
||||
/* allow inclusion of ,: space tab to key */
|
||||
if (fmt[1] == '\0')
|
||||
jp->error = JPRINT_FMT;
|
||||
else {
|
||||
++fmt;
|
||||
if (k < KEYLEN) {
|
||||
key[k++] = *fmt;
|
||||
key[k] = '\0';
|
||||
} else
|
||||
jp->error = JPRINT_FMT;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (k < KEYLEN) {
|
||||
key[k++] = *fmt;
|
||||
key[k] = '\0';
|
||||
} else
|
||||
jp->error = JPRINT_FMT;
|
||||
break;
|
||||
}
|
||||
++fmt;
|
||||
}
|
||||
va_end(ap);
|
||||
if (jp->error != JPRINT_OK)
|
||||
return (-1);
|
||||
|
||||
return ((int)(jp->bufp - start));
|
||||
}
|
|
@ -28,284 +28,11 @@
|
|||
#include <sys/vdev_impl.h>
|
||||
#include <sys/spa.h>
|
||||
#include <zfs_comutil.h>
|
||||
#include <sys/jprint.h>
|
||||
#include <sys/spa_json_stats.h>
|
||||
#include <sys/nvpair_impl.h>
|
||||
|
||||
#define JSON_STATUS_VERSION 4
|
||||
|
||||
/*
|
||||
* Return string for datatype -- this guides us in implementing
|
||||
* json translation.
|
||||
*/
|
||||
static const char *
|
||||
datatype_string(data_type_t t)
|
||||
{
|
||||
switch (t) {
|
||||
case DATA_TYPE_UNKNOWN: return "DATA_TYPE_UNKNOWN";
|
||||
case DATA_TYPE_BOOLEAN: return "DATA_TYPE_BOOLEAN";
|
||||
case DATA_TYPE_BYTE: return "DATA_TYPE_BYTE";
|
||||
case DATA_TYPE_INT16: return "DATA_TYPE_INT16";
|
||||
case DATA_TYPE_UINT16: return "DATA_TYPE_UINT16";
|
||||
case DATA_TYPE_INT32: return "DATA_TYPE_INT32";
|
||||
case DATA_TYPE_UINT32: return "DATA_TYPE_UINT32";
|
||||
case DATA_TYPE_INT64: return "DATA_TYPE_INT64";
|
||||
case DATA_TYPE_UINT64: return "DATA_TYPE_UINT64";
|
||||
case DATA_TYPE_STRING: return "DATA_TYPE_STRING";
|
||||
case DATA_TYPE_BYTE_ARRAY: return "DATA_TYPE_BYTE_ARRAY";
|
||||
case DATA_TYPE_INT16_ARRAY: return "DATA_TYPE_INT16_ARRAY";
|
||||
case DATA_TYPE_UINT16_ARRAY: return "DATA_TYPE_UINT16_ARRAY";
|
||||
case DATA_TYPE_INT32_ARRAY: return "DATA_TYPE_INT32_ARRAY";
|
||||
case DATA_TYPE_UINT32_ARRAY: return "DATA_TYPE_UINT32_ARRAY";
|
||||
case DATA_TYPE_INT64_ARRAY: return "DATA_TYPE_INT64_ARRAY";
|
||||
case DATA_TYPE_UINT64_ARRAY: return "DATA_TYPE_UINT64_ARRAY";
|
||||
case DATA_TYPE_STRING_ARRAY: return "DATA_TYPE_STRING_ARRAY";
|
||||
case DATA_TYPE_HRTIME: return "DATA_TYPE_HRTIME";
|
||||
case DATA_TYPE_NVLIST: return "DATA_TYPE_NVLIST";
|
||||
case DATA_TYPE_NVLIST_ARRAY: return "DATA_TYPE_NVLIST_ARRAY";
|
||||
case DATA_TYPE_BOOLEAN_VALUE: return "DATA_TYPE_BOOLEAN_VALUE";
|
||||
case DATA_TYPE_INT8: return "DATA_TYPE_INT8";
|
||||
case DATA_TYPE_UINT8: return "DATA_TYPE_UINT8";
|
||||
case DATA_TYPE_BOOLEAN_ARRAY: return "DATA_TYPE_BOOLEAN_ARRAY";
|
||||
case DATA_TYPE_INT8_ARRAY: return "DATA_TYPE_INT8_ARRAY";
|
||||
case DATA_TYPE_UINT8_ARRAY: return "DATA_TYPE_UINT8_ARRAY";
|
||||
default: return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* nvlist_to_json takes a filter function. If the functions returns
|
||||
* B_TRUE, the case has been handled. If it returns B_FALSE, the
|
||||
* case has not been handled, and will be handled. Invoking nvlist_to_json
|
||||
* with a NULL filter chooses the default filter, which does nothing.
|
||||
*
|
||||
* The filtering is passed the jprint_t in case the nesting level is
|
||||
* important, name, data type and value.
|
||||
*/
|
||||
typedef boolean_t nvj_filter_t(jprint_t *, const char *, data_type_t, void *);
|
||||
|
||||
static boolean_t
|
||||
null_filter(jprint_t *jp, const char *name, data_type_t type, void *value)
|
||||
{
|
||||
jp = jp; name = name; type = type; value = value;
|
||||
return (B_FALSE);
|
||||
}
|
||||
|
||||
static void nvlist_to_json(nvlist_t *nvl, jprint_t *jp, nvj_filter_t f);
|
||||
|
||||
/*
|
||||
* Convert source (src) to string -- up to 105 characters, so pass in 256
|
||||
* byte buffer (for future)
|
||||
*/
|
||||
static void
|
||||
source_to_string(uint64_t src, char *buf)
|
||||
{
|
||||
buf[0] = '\0';
|
||||
if (src & ZPROP_SRC_NONE) {
|
||||
if (buf[0] != '\0')
|
||||
strcat(buf, "|");
|
||||
strcat(buf, "ZPROP_SRC_NONE");
|
||||
}
|
||||
if (src & ZPROP_SRC_DEFAULT) {
|
||||
if (buf[0] != '\0')
|
||||
strcat(buf, "|");
|
||||
strcat(buf, "ZPROP_SRC_DEFAULT");
|
||||
}
|
||||
if (src & ZPROP_SRC_TEMPORARY) {
|
||||
if (buf[0] != '\0')
|
||||
strcat(buf, "|");
|
||||
strcat(buf, "ZPROP_SRC_TEMPORARY");
|
||||
}
|
||||
if (src & ZPROP_SRC_INHERITED) {
|
||||
if (buf[0] != '\0')
|
||||
strcat(buf, "|");
|
||||
strcat(buf, "ZPROP_SRC_INHERITED");
|
||||
}
|
||||
if (src & ZPROP_SRC_RECEIVED) {
|
||||
if (buf[0] != '\0')
|
||||
strcat(buf, "|");
|
||||
strcat(buf, "ZPROP_SRC_RECEIVED");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* spa_props_filter replace source: with string. The way source is
|
||||
* defined it could be bitmap -- so generate the | sequence as
|
||||
* needed.
|
||||
*/
|
||||
static boolean_t
|
||||
spa_props_filter(jprint_t *jp, const char *name, data_type_t type, void *value)
|
||||
{
|
||||
if ((strcmp(name, "source") == 0) &&
|
||||
(type == DATA_TYPE_UINT64)) {
|
||||
uint64_t src = *(uint64_t *)value;
|
||||
char buf[256];
|
||||
source_to_string(src, buf);
|
||||
jp_printf(jp, "source: %s", buf);
|
||||
return (B_TRUE);
|
||||
}
|
||||
return (B_FALSE);
|
||||
}
|
||||
|
||||
/*
|
||||
* stats_filter removes parts of the nvlist we don't want to visit.
|
||||
*/
|
||||
static boolean_t
|
||||
stats_filter(jprint_t *jp, const char *name, data_type_t type, void *value)
|
||||
{
|
||||
/*
|
||||
* Suppress root object state:
|
||||
*/
|
||||
if ((jp->stackp == 0) &&
|
||||
(type == DATA_TYPE_UINT64) &&
|
||||
(strcmp(name, "state") == 0))
|
||||
return (B_TRUE);
|
||||
|
||||
/*
|
||||
* Suppress root object vdev_children: -- we will
|
||||
* output at one level down.
|
||||
*/
|
||||
if ((jp->stackp == 0) &&
|
||||
(type == DATA_TYPE_UINT64) &&
|
||||
(strcmp(name, "vdev_children") == 0))
|
||||
return (B_TRUE);
|
||||
|
||||
/*
|
||||
* We are going to suppress vdev_tree:, and generate the
|
||||
* data ourselves.
|
||||
* It does seem like a bit of a waste going through this
|
||||
* twice... but for now, this seems prudent.
|
||||
*/
|
||||
if ((jp->stackp == 0) &&
|
||||
(type == DATA_TYPE_NVLIST) &&
|
||||
(strcmp(name, "vdev_tree") == 0))
|
||||
return (B_TRUE);
|
||||
|
||||
/*
|
||||
* Process spa_props:. Here we recurse, but with a filter that
|
||||
* modifies source.
|
||||
*/
|
||||
if ((jp->stackp == 0) &&
|
||||
(type == DATA_TYPE_NVLIST) &&
|
||||
(strcmp(name, "spa_props") == 0)) {
|
||||
jp_printf(jp, "spa_props: {");
|
||||
nvlist_to_json((nvlist_t *)value, jp, spa_props_filter);
|
||||
jp_printf(jp, "}");
|
||||
return (B_TRUE);
|
||||
}
|
||||
|
||||
return (B_FALSE);
|
||||
}
|
||||
|
||||
/*
|
||||
* This code is NOT hightly abstracted -- just some duplication, until I
|
||||
* actually understand what is needed.
|
||||
*
|
||||
* In avoiding early abstraction, we find the need for a "filter". Which
|
||||
* is now implemented. This does appear in the spirit of other ZFS
|
||||
* coding.
|
||||
*/
|
||||
static void
|
||||
nvlist_to_json(nvlist_t *nvl, jprint_t *jp, nvj_filter_t f)
|
||||
{
|
||||
const nvpriv_t *priv;
|
||||
const i_nvp_t *curr;
|
||||
uint64_t *u = NULL;
|
||||
nvlist_t **a = NULL;
|
||||
|
||||
if (f == NULL)
|
||||
f = null_filter;
|
||||
|
||||
if ((priv = (const nvpriv_t *)(uintptr_t)nvl->nvl_priv) == NULL)
|
||||
return;
|
||||
|
||||
for (curr = priv->nvp_list; curr != NULL; curr = curr->nvi_next) {
|
||||
const nvpair_t *nvp = &curr->nvi_nvp;
|
||||
const char *name = (const char *)NVP_NAME(nvp);
|
||||
data_type_t type = NVP_TYPE(nvp);
|
||||
void *p = NVP_VALUE(nvp);
|
||||
|
||||
if (jp_error(jp) != JPRINT_OK)
|
||||
return;
|
||||
|
||||
if (f(jp, name, type, p))
|
||||
continue;
|
||||
switch (type) {
|
||||
|
||||
/*
|
||||
* Array types
|
||||
*/
|
||||
case DATA_TYPE_UINT64_ARRAY:
|
||||
u = (uint64_t *)p;
|
||||
jp_printf(jp, "%k: [", name);
|
||||
for (int i = 0; i < NVP_NELEM(nvp); ++i) {
|
||||
if (jp_error(jp) != JPRINT_OK)
|
||||
break;
|
||||
jp_printf(jp, "%U", u[i]);
|
||||
}
|
||||
jp_printf(jp, "]");
|
||||
break;
|
||||
case DATA_TYPE_NVLIST_ARRAY:
|
||||
a = (nvlist_t **)p;
|
||||
jp_printf(jp, "%k: [", name);
|
||||
for (int i = 0; i < NVP_NELEM(nvp); ++i) {
|
||||
if (jp_error(jp) != JPRINT_OK)
|
||||
break;
|
||||
jp_printf(jp, "{");
|
||||
nvlist_to_json(a[i], jp, f);
|
||||
jp_printf(jp, "}");
|
||||
}
|
||||
jp_printf(jp, "]");
|
||||
break;
|
||||
|
||||
/*
|
||||
* Primitive types
|
||||
*/
|
||||
case DATA_TYPE_UINT64:
|
||||
jp_printf(jp, "%k: %U", name, *(uint64_t *)p);
|
||||
break;
|
||||
case DATA_TYPE_INT64:
|
||||
jp_printf(jp, "%k: %D", name, *(int64_t *)p);
|
||||
break;
|
||||
case DATA_TYPE_UINT32:
|
||||
jp_printf(jp, "%k: %u", name, *(uint32_t *)p);
|
||||
break;
|
||||
case DATA_TYPE_INT32:
|
||||
jp_printf(jp, "%k: %d", name, *(int32_t *)p);
|
||||
break;
|
||||
case DATA_TYPE_STRING:
|
||||
jp_printf(jp, "%k: %s", name, (char *)p);
|
||||
break;
|
||||
case DATA_TYPE_BOOLEAN:
|
||||
jp_printf(jp, "%k: %b", name, B_TRUE);
|
||||
break;
|
||||
case DATA_TYPE_BOOLEAN_VALUE:
|
||||
jp_printf(jp, "%k: %b", name, *(boolean_t *)p);
|
||||
break;
|
||||
|
||||
/*
|
||||
* Object types
|
||||
*/
|
||||
case DATA_TYPE_NVLIST:
|
||||
jp_printf(jp, "%k: {", name);
|
||||
nvlist_to_json((nvlist_t *)p, jp, f);
|
||||
jp_printf(jp, "}");
|
||||
break;
|
||||
|
||||
/*
|
||||
* Default -- tell us what we are missing. This is done to
|
||||
* simply avoid writing ALL the cases, YAGNI (yah ain't
|
||||
* gonna need it).
|
||||
*/
|
||||
default:
|
||||
jp_printf(jp, "%k: %s", name, datatype_string(type));
|
||||
zfs_dbgmsg("name = %s type = %d %s", name,
|
||||
(int)type, datatype_string(type));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const char *
|
||||
vdev_state_string(vdev_state_t state, vdev_aux_t aux)
|
||||
{
|
||||
|
@ -332,67 +59,71 @@ vdev_state_string(vdev_state_t state, vdev_aux_t aux)
|
|||
}
|
||||
|
||||
static void
|
||||
vdev_to_json(vdev_t *vd, pool_scan_stat_t *ps, jprint_t *jp)
|
||||
vdev_to_nvlist(vdev_t *vd, pool_scan_stat_t *ps, nvlist_t *tree)
|
||||
{
|
||||
uint64_t i, n;
|
||||
uint64_t n;
|
||||
int nparity = vdev_get_nparity(vd);
|
||||
vdev_t **a;
|
||||
const char *s;
|
||||
nvlist_t *init_state, *trim_state;
|
||||
|
||||
jp_printf(jp, "type: %s", vd->vdev_ops->vdev_op_type);
|
||||
nvlist_add_string(tree, "type", vd->vdev_ops->vdev_op_type);
|
||||
|
||||
/* dRAID vdevs have additional config keys */
|
||||
if (vd->vdev_ops == &vdev_draid_ops &&
|
||||
vd->vdev_ops->vdev_op_config_generate != NULL) {
|
||||
nvlist_t *nvl = fnvlist_alloc();
|
||||
vd->vdev_ops->vdev_op_config_generate(vd, nvl);
|
||||
nvlist_to_json(nvl, jp, null_filter);
|
||||
fnvlist_merge(tree, nvl);
|
||||
nvlist_free(nvl);
|
||||
} else if (nparity > 0) {
|
||||
/* RAIDZ parity */
|
||||
jp_printf(jp, "nparity: %U", nparity);
|
||||
fnvlist_add_uint64(tree, "nparity", nparity);
|
||||
}
|
||||
jp_printf(jp, "id: %U", vd->vdev_id);
|
||||
jp_printf(jp, "guid: %U", vd->vdev_guid);
|
||||
|
||||
fnvlist_add_uint64(tree, "id", vd->vdev_id);
|
||||
fnvlist_add_uint64(tree, "guid", vd->vdev_guid);
|
||||
if (strcmp(vd->vdev_ops->vdev_op_type, "root") != 0) {
|
||||
jp_printf(jp, "asize: %U", vd->vdev_asize);
|
||||
jp_printf(jp, "ashift: %U", vd->vdev_ashift);
|
||||
fnvlist_add_uint64(tree, "asize", vd->vdev_asize);
|
||||
fnvlist_add_uint64(tree, "ashift", vd->vdev_ashift);
|
||||
if (vd->vdev_ops->vdev_op_leaf) {
|
||||
jp_printf(jp, "whole_disk: %b",
|
||||
fnvlist_add_boolean_value(tree, "whole_disk",
|
||||
(vd->vdev_wholedisk == 0) ? B_FALSE : B_TRUE);
|
||||
}
|
||||
jp_printf(jp, "offline: %b",
|
||||
fnvlist_add_boolean_value(tree, "offline",
|
||||
(vd->vdev_offline == 0) ? B_FALSE : B_TRUE);
|
||||
jp_printf(jp, "faulted: %b",
|
||||
fnvlist_add_boolean_value(tree, "faulted",
|
||||
(vd->vdev_faulted == 0) ? B_FALSE : B_TRUE);
|
||||
jp_printf(jp, "degraded: %b",
|
||||
fnvlist_add_boolean_value(tree, "degraded",
|
||||
(vd->vdev_degraded == 0) ? B_FALSE : B_TRUE);
|
||||
jp_printf(jp, "removed: %b",
|
||||
fnvlist_add_boolean_value(tree, "removed",
|
||||
(vd->vdev_removed == 0) ? B_FALSE : B_TRUE);
|
||||
jp_printf(jp, "not_present: %b",
|
||||
fnvlist_add_boolean_value(tree, "not_present",
|
||||
(vd->vdev_not_present == 0) ? B_FALSE : B_TRUE);
|
||||
jp_printf(jp, "is_log: %b",
|
||||
fnvlist_add_boolean_value(tree, "is_log",
|
||||
(vd->vdev_islog == 0) ? B_FALSE : B_TRUE);
|
||||
|
||||
if (vd->vdev_path != NULL)
|
||||
jp_printf(jp, "path: %s", vd->vdev_path);
|
||||
fnvlist_add_string(tree, "path", vd->vdev_path);
|
||||
if (vd->vdev_devid != NULL)
|
||||
jp_printf(jp, "devid: %s", vd->vdev_devid);
|
||||
fnvlist_add_string(tree, "devid", vd->vdev_devid);
|
||||
if (vd->vdev_physpath != NULL)
|
||||
jp_printf(jp, "physpath: %s", vd->vdev_physpath);
|
||||
if (vd->vdev_enc_sysfs_path != NULL)
|
||||
jp_printf(jp, "enc_sysfs_path: %s",
|
||||
fnvlist_add_string(tree, "physpath", vd->vdev_physpath);
|
||||
if (vd->vdev_enc_sysfs_path != NULL) {
|
||||
fnvlist_add_string(tree, "enc_sysfs_path",
|
||||
vd->vdev_enc_sysfs_path);
|
||||
jp_printf(jp, "state: %s", vdev_state_string(vd->vdev_state,
|
||||
vd->vdev_stat.vs_aux));
|
||||
}
|
||||
fnvlist_add_string(tree, "state",
|
||||
vdev_state_string(vd->vdev_state, vd->vdev_stat.vs_aux));
|
||||
/*
|
||||
* Try for some of the extended status annotations that
|
||||
* zpool status provides.
|
||||
*/
|
||||
jp_printf(jp, "vs_scan_removing: %b",
|
||||
fnvlist_add_boolean_value(tree, "vs_scan_removing",
|
||||
vd->vdev_stat.vs_scan_removing != 0);
|
||||
jp_printf(jp, "vs_noalloc: %b", vd->vdev_stat.vs_noalloc != 0);
|
||||
jp_printf(jp, "vs_resilver_deferred: %b",
|
||||
fnvlist_add_boolean_value(tree, "vs_noalloc",
|
||||
vd->vdev_stat.vs_noalloc != 0);
|
||||
fnvlist_add_boolean_value(tree, "vs_resilver_deferred",
|
||||
vd->vdev_stat.vs_resilver_deferred);
|
||||
s = "none";
|
||||
if ((vd->vdev_state == VDEV_STATE_UNKNOWN) ||
|
||||
|
@ -409,8 +140,9 @@ vdev_to_json(vdev_t *vd, pool_scan_stat_t *ps, jprint_t *jp)
|
|||
}
|
||||
}
|
||||
}
|
||||
jp_printf(jp, "resilver_repair: %s", s);
|
||||
jp_printf(jp, "initialize_state: {");
|
||||
fnvlist_add_string(tree, "resilver_repair", s);
|
||||
|
||||
init_state = fnvlist_alloc();
|
||||
s = "VDEV_INITIALIZE_NONE";
|
||||
if (vd->vdev_stat.vs_initialize_state == VDEV_INITIALIZE_ACTIVE)
|
||||
s = "VDEV_INITIALIZE_ACTIVE";
|
||||
|
@ -420,16 +152,17 @@ vdev_to_json(vdev_t *vd, pool_scan_stat_t *ps, jprint_t *jp)
|
|||
if (vd->vdev_stat.vs_initialize_state ==
|
||||
VDEV_INITIALIZE_COMPLETE)
|
||||
s = "VDEV_INITIALIZE_COMPLETE";
|
||||
jp_printf(jp, "vs_initialize_state: %s", s);
|
||||
jp_printf(jp, "vs_initialize_bytes_done: %U",
|
||||
fnvlist_add_string(init_state, "vs_initialize_state", s);
|
||||
fnvlist_add_uint64(init_state, "vs_initialize_bytes_done:",
|
||||
vd->vdev_stat.vs_initialize_bytes_done);
|
||||
jp_printf(jp, "vs_initialize_bytes_est: %U",
|
||||
fnvlist_add_uint64(init_state, "vs_initialize_bytes_est",
|
||||
vd->vdev_stat.vs_initialize_bytes_est);
|
||||
jp_printf(jp, "vs_initialize_action_time: %U",
|
||||
fnvlist_add_uint64(init_state, "vs_initialize_action_time",
|
||||
vd->vdev_stat.vs_initialize_action_time);
|
||||
jp_printf(jp, "}");
|
||||
fnvlist_add_nvlist(tree, "initialize_state", init_state);
|
||||
fnvlist_free(init_state);
|
||||
|
||||
jp_printf(jp, "trim_state: {");
|
||||
trim_state = fnvlist_alloc();
|
||||
s = "VDEV_UNTRIMMED";
|
||||
if (vd->vdev_stat.vs_trim_state == VDEV_TRIM_ACTIVE)
|
||||
s = "VDEV_TRIM_ACTIVE";
|
||||
|
@ -439,53 +172,88 @@ vdev_to_json(vdev_t *vd, pool_scan_stat_t *ps, jprint_t *jp)
|
|||
s = "VDEV_TRIM_COMPLETE";
|
||||
if (vd->vdev_stat.vs_trim_notsup)
|
||||
s = "VDEV_TRIM_UNSUPPORTED";
|
||||
jp_printf(jp, "vs_trim_state: %s", s);
|
||||
fnvlist_add_string(trim_state, "vs_trim_state", s);
|
||||
if (!vd->vdev_stat.vs_trim_notsup) {
|
||||
jp_printf(jp, "vs_trim_action_time: %U",
|
||||
fnvlist_add_uint64(trim_state, "vs_trim_action_time",
|
||||
vd->vdev_stat.vs_trim_action_time);
|
||||
jp_printf(jp, "vs_trim_bytes_done: %U",
|
||||
fnvlist_add_uint64(trim_state, "vs_trim_bytes_done",
|
||||
vd->vdev_stat.vs_trim_bytes_done);
|
||||
jp_printf(jp, "vs_trim_bytes_est: %U",
|
||||
fnvlist_add_uint64(trim_state, "vs_trim_bytes_est",
|
||||
vd->vdev_stat.vs_trim_bytes_est);
|
||||
}
|
||||
jp_printf(jp, "}");
|
||||
fnvlist_add_nvlist(tree, "trim_state", trim_state);
|
||||
fnvlist_free(trim_state);
|
||||
|
||||
jp_printf(jp, "read_errors: %U",
|
||||
fnvlist_add_uint64(tree, "read_errors",
|
||||
vd->vdev_stat.vs_read_errors);
|
||||
jp_printf(jp, "write_errors: %U",
|
||||
fnvlist_add_uint64(tree, "write_errors",
|
||||
vd->vdev_stat.vs_write_errors);
|
||||
jp_printf(jp, "checksum_errors: %U",
|
||||
fnvlist_add_uint64(tree, "checksum_errors",
|
||||
vd->vdev_stat.vs_checksum_errors);
|
||||
jp_printf(jp, "slow_ios: %U",
|
||||
fnvlist_add_uint64(tree, "slow_ios",
|
||||
vd->vdev_stat.vs_slow_ios);
|
||||
jp_printf(jp, "trim_errors: %U",
|
||||
fnvlist_add_uint64(tree, "trim_errors",
|
||||
vd->vdev_stat.vs_trim_errors);
|
||||
}
|
||||
n = vd->vdev_children;
|
||||
a = vd->vdev_child;
|
||||
if (n != 0) {
|
||||
jp_printf(jp, "vdev_children: %U", n);
|
||||
jp_printf(jp, "children: [");
|
||||
for (i = 0; i < n; ++i) {
|
||||
jp_printf(jp, "{");
|
||||
vdev_to_json(a[i], ps, jp);
|
||||
jp_printf(jp, "}");
|
||||
fnvlist_add_uint64(tree, "vdev_children", n);
|
||||
nvlist_t **ch = kmem_alloc(sizeof (nvlist_t *) * n, KM_NOSLEEP);
|
||||
for (uint64_t i = 0; i < n; ++i) {
|
||||
ch[i] = fnvlist_alloc();
|
||||
vdev_to_nvlist(a[i], ps, ch[i]);
|
||||
}
|
||||
jp_printf(jp, "]");
|
||||
fnvlist_add_nvlist_array(tree, "children",
|
||||
(const nvlist_t * const *) ch, n);
|
||||
for (uint64_t i = 0; i < n; ++i)
|
||||
fnvlist_free(ch[i]);
|
||||
kmem_free(ch, sizeof (nvlist_t *) * n);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
iterate_vdevs(spa_t *spa, pool_scan_stat_t *ps, jprint_t *jp)
|
||||
iterate_vdevs(spa_t *spa, pool_scan_stat_t *ps, nvlist_t *nvl)
|
||||
{
|
||||
nvlist_t *vt = fnvlist_alloc();
|
||||
vdev_t *v = spa->spa_root_vdev;
|
||||
if (v == NULL) {
|
||||
jp_printf(jp, "error: %s", "NO ROOT VDEV");
|
||||
zfs_dbgmsg("error: NO ROOT VDEV");
|
||||
return;
|
||||
}
|
||||
jp_printf(jp, "vdev_tree: {");
|
||||
vdev_to_json(v, ps, jp);
|
||||
jp_printf(jp, "}");
|
||||
vdev_to_nvlist(v, ps, vt);
|
||||
int nspares = spa->spa_spares.sav_count;
|
||||
if (nspares != 0) {
|
||||
nvlist_t **sp = kmem_alloc(sizeof (nvlist_t *) * nspares,
|
||||
KM_NOSLEEP);
|
||||
for (int i = 0; i < nspares; i++) {
|
||||
v = spa->spa_spares.sav_vdevs[i];
|
||||
sp[i] = fnvlist_alloc();
|
||||
vdev_to_nvlist(v, ps, sp[i]);
|
||||
}
|
||||
fnvlist_add_nvlist_array(vt, ZPOOL_CONFIG_SPARES,
|
||||
(const nvlist_t * const *) sp, nspares);
|
||||
for (int i = 0; i < nspares; i++)
|
||||
fnvlist_free(sp[i]);
|
||||
kmem_free(sp, sizeof (nvlist_t *) * nspares);
|
||||
}
|
||||
int nl2cache = spa->spa_l2cache.sav_count;
|
||||
if (nl2cache != 0) {
|
||||
nvlist_t **l2 = kmem_alloc(sizeof (nvlist_t *) * nl2cache,
|
||||
KM_NOSLEEP);
|
||||
for (int i = 0; i < nl2cache; i++) {
|
||||
v = spa->spa_l2cache.sav_vdevs[i];
|
||||
l2[i] = fnvlist_alloc();
|
||||
vdev_to_nvlist(v, ps, l2[i]);
|
||||
}
|
||||
fnvlist_add_nvlist_array(vt, ZPOOL_CONFIG_L2CACHE,
|
||||
(const nvlist_t * const *) l2, nl2cache);
|
||||
for (int i = 0; i < nspares; i++)
|
||||
fnvlist_free(l2[i]);
|
||||
kmem_free(l2, sizeof (nvlist_t *) * nl2cache);
|
||||
}
|
||||
fnvlist_add_nvlist(nvl, "vdev_tree", vt);
|
||||
fnvlist_free(vt);
|
||||
}
|
||||
|
||||
static const char *
|
||||
|
@ -514,6 +282,68 @@ static const char *pss_state_to_string(uint64_t n)
|
|||
return (s);
|
||||
}
|
||||
|
||||
static int
|
||||
spa_props_json(spa_t *spa, nvlist_t **nvl)
|
||||
{
|
||||
nvpair_t *curr = NULL, *item = NULL;
|
||||
nvlist_t *prop;
|
||||
data_type_t type;
|
||||
char buf[256];
|
||||
const char *name;
|
||||
uint64_t src;
|
||||
|
||||
if (spa_prop_get(spa, nvl) != 0)
|
||||
return (-1);
|
||||
|
||||
for (curr = nvlist_next_nvpair(*nvl, NULL); curr;
|
||||
curr = nvlist_next_nvpair(*nvl, curr)) {
|
||||
if (nvpair_type(curr) == DATA_TYPE_NVLIST) {
|
||||
prop = fnvpair_value_nvlist(curr);
|
||||
for (item = nvlist_next_nvpair(prop, NULL); item;
|
||||
item = nvlist_next_nvpair(prop, item)) {
|
||||
name = nvpair_name(item);
|
||||
type = nvpair_type(item);
|
||||
if ((strcmp(name, "source") == 0) &&
|
||||
(type == DATA_TYPE_UINT64)) {
|
||||
src = fnvpair_value_uint64(item);
|
||||
memset(buf, 0, 256);
|
||||
if (src & ZPROP_SRC_NONE) {
|
||||
if (buf[0] != '\0')
|
||||
strcat(buf, "|");
|
||||
strcat(buf, "ZPROP_SRC_NONE");
|
||||
}
|
||||
if (src & ZPROP_SRC_DEFAULT) {
|
||||
if (buf[0] != '\0')
|
||||
strcat(buf, "|");
|
||||
strcat(buf,
|
||||
"ZPROP_SRC_DEFAULT");
|
||||
}
|
||||
if (src & ZPROP_SRC_TEMPORARY) {
|
||||
if (buf[0] != '\0')
|
||||
strcat(buf, "|");
|
||||
strcat(buf,
|
||||
"ZPROP_SRC_TEMPORARY");
|
||||
}
|
||||
if (src & ZPROP_SRC_INHERITED) {
|
||||
if (buf[0] != '\0')
|
||||
strcat(buf, "|");
|
||||
strcat(buf,
|
||||
"ZPROP_SRC_INHERITED");
|
||||
}
|
||||
if (src & ZPROP_SRC_RECEIVED) {
|
||||
if (buf[0] != '\0')
|
||||
strcat(buf, "|");
|
||||
strcat(buf,
|
||||
"ZPROP_SRC_RECEIVED");
|
||||
}
|
||||
fnvlist_add_string(prop, "source", buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Collect the spa status without any locking and return as a JSON string.
|
||||
*
|
||||
|
@ -524,32 +354,36 @@ spa_generate_json_stats(spa_t *spa, char *buf, size_t size)
|
|||
{
|
||||
int error = 0;
|
||||
int ps_error = 0;
|
||||
jprint_t jp;
|
||||
nvlist_t *nvl, *pnvl;
|
||||
char *curr = buf;
|
||||
nvlist_t *spa_config, *spa_props = NULL, *scan_stats, *nvl;
|
||||
uint64_t loadtimes[2];
|
||||
pool_scan_stat_t ps;
|
||||
int scl_config_lock;
|
||||
|
||||
if (nvlist_dup(spa->spa_config, &nvl, 0) != 0) {
|
||||
nvl = fnvlist_alloc();
|
||||
if (nvlist_dup(spa->spa_config, &spa_config, 0) != 0) {
|
||||
zfs_dbgmsg("json_data: nvlist_dup failed");
|
||||
return (0);
|
||||
}
|
||||
fnvlist_add_nvlist(nvl, ZPOOL_CONFIG_LOAD_INFO, spa->spa_load_info);
|
||||
fnvlist_add_nvlist(spa_config, ZPOOL_CONFIG_LOAD_INFO,
|
||||
spa->spa_load_info);
|
||||
|
||||
scl_config_lock =
|
||||
spa_config_tryenter(spa, SCL_CONFIG, FTAG, RW_READER);
|
||||
|
||||
ps_error = spa_scan_get_stats(spa, &ps);
|
||||
(void) ps_error;
|
||||
|
||||
error = spa_prop_get(spa, &pnvl);
|
||||
fnvlist_add_nvlist(nvl, "spa_props", pnvl);
|
||||
if (spa_props_json(spa, &spa_props) == 0)
|
||||
fnvlist_add_nvlist(spa_config, "spa_props", spa_props);
|
||||
|
||||
loadtimes[0] = spa->spa_loaded_ts.tv_sec;
|
||||
loadtimes[1] = spa->spa_loaded_ts.tv_nsec;
|
||||
fnvlist_add_uint64_array(nvl, ZPOOL_CONFIG_LOADED_TIME, loadtimes, 2);
|
||||
fnvlist_add_uint64(nvl, ZPOOL_CONFIG_ERRCOUNT,
|
||||
fnvlist_add_uint64_array(spa_config, ZPOOL_CONFIG_LOADED_TIME,
|
||||
loadtimes, 2);
|
||||
fnvlist_add_uint64(spa_config, ZPOOL_CONFIG_ERRCOUNT,
|
||||
spa_approx_errlog_size(spa));
|
||||
fnvlist_add_boolean_value(nvl, ZPOOL_CONFIG_SUSPENDED,
|
||||
fnvlist_add_boolean_value(spa_config, ZPOOL_CONFIG_SUSPENDED,
|
||||
spa_suspended(spa));
|
||||
if (spa_suspended(spa)) {
|
||||
const char *failmode;
|
||||
|
@ -566,84 +400,67 @@ spa_generate_json_stats(spa_t *spa, char *buf, size_t size)
|
|||
default:
|
||||
failmode = "???";
|
||||
}
|
||||
fnvlist_add_string(nvl, "failmode", failmode);
|
||||
fnvlist_add_string(spa_config, "failmode", failmode);
|
||||
if (spa->spa_suspended != ZIO_SUSPEND_NONE) {
|
||||
fnvlist_add_string(nvl, ZPOOL_CONFIG_SUSPENDED_REASON,
|
||||
fnvlist_add_string(spa_config,
|
||||
ZPOOL_CONFIG_SUSPENDED_REASON,
|
||||
(spa->spa_suspended == ZIO_SUSPEND_MMP) ?
|
||||
"MMP" : "IO");
|
||||
}
|
||||
}
|
||||
|
||||
jp_open(&jp, buf, size);
|
||||
jp_printf(&jp, "{");
|
||||
fnvlist_add_uint32(nvl, "status_json_version", JSON_STATUS_VERSION);
|
||||
fnvlist_add_boolean_value(nvl, "scl_config_lock", scl_config_lock != 0);
|
||||
fnvlist_add_uint32(nvl, "scan_error", ps_error);
|
||||
|
||||
jp_printf(&jp, "status_json_version: %d", JSON_STATUS_VERSION);
|
||||
jp_printf(&jp, "scl_config_lock: %b", scl_config_lock != 0);
|
||||
jp_printf(&jp, "scan_error: %d", ps_error);
|
||||
jp_printf(&jp, "scan_stats: {");
|
||||
scan_stats = fnvlist_alloc();
|
||||
if (ps_error == 0) {
|
||||
jp_printf(&jp, "func: %s", pss_func_to_string(ps.pss_func));
|
||||
jp_printf(&jp, "state: %s", pss_state_to_string(ps.pss_state));
|
||||
jp_printf(&jp, "start_time: %U", ps.pss_start_time);
|
||||
jp_printf(&jp, "end_time: %U", ps.pss_end_time);
|
||||
jp_printf(&jp, "to_examine: %U", ps.pss_to_examine);
|
||||
jp_printf(&jp, "examined: %U", ps.pss_examined);
|
||||
jp_printf(&jp, "processed: %U", ps.pss_processed);
|
||||
jp_printf(&jp, "errors: %U", ps.pss_errors);
|
||||
|
||||
jp_printf(&jp, "pass_exam: %U", ps.pss_pass_exam);
|
||||
jp_printf(&jp, "pass_start: %U", ps.pss_pass_start);
|
||||
jp_printf(&jp, "pass_scrub_pause: %U", ps.pss_pass_scrub_pause);
|
||||
jp_printf(&jp, "pass_scrub_spent_paused: %U",
|
||||
fnvlist_add_string(scan_stats, "func",
|
||||
pss_func_to_string(ps.pss_func));
|
||||
fnvlist_add_string(scan_stats, "state",
|
||||
pss_state_to_string(ps.pss_state));
|
||||
fnvlist_add_uint64(scan_stats, "start_time", ps.pss_start_time);
|
||||
fnvlist_add_uint64(scan_stats, "end_time", ps.pss_end_time);
|
||||
fnvlist_add_uint64(scan_stats, "to_examine", ps.pss_to_examine);
|
||||
fnvlist_add_uint64(scan_stats, "examined", ps.pss_examined);
|
||||
fnvlist_add_uint64(scan_stats, "processed", ps.pss_processed);
|
||||
fnvlist_add_uint64(scan_stats, "errors", ps.pss_errors);
|
||||
fnvlist_add_uint64(scan_stats, "pass_exam", ps.pss_pass_exam);
|
||||
fnvlist_add_uint64(scan_stats, "pass_start", ps.pss_pass_start);
|
||||
fnvlist_add_uint64(scan_stats, "pass_scrub_pause",
|
||||
ps.pss_pass_scrub_pause);
|
||||
fnvlist_add_uint64(scan_stats, "pass_scrub_spent_paused",
|
||||
ps.pss_pass_scrub_spent_paused);
|
||||
jp_printf(&jp, "pass_issued: %U", ps.pss_pass_issued);
|
||||
jp_printf(&jp, "issued: %U", ps.pss_issued);
|
||||
fnvlist_add_uint64(scan_stats, "pass_issued",
|
||||
ps.pss_pass_issued);
|
||||
fnvlist_add_uint64(scan_stats, "issued", ps.pss_issued);
|
||||
} else if (ps_error == ENOENT) {
|
||||
jp_printf(&jp, "func: %s", "NONE");
|
||||
jp_printf(&jp, "state: %s", "NONE");
|
||||
fnvlist_add_string(scan_stats, "func", "NONE");
|
||||
fnvlist_add_string(scan_stats, "state", "NONE");
|
||||
} else {
|
||||
jp_printf(&jp, "func: %s", "?");
|
||||
jp_printf(&jp, "state: %s", "?");
|
||||
fnvlist_add_string(scan_stats, "func", "NONE");
|
||||
fnvlist_add_string(scan_stats, "state", "NONE");
|
||||
}
|
||||
jp_printf(&jp, "}");
|
||||
fnvlist_add_nvlist(nvl, "scan_stats", scan_stats);
|
||||
fnvlist_add_string(nvl, "state", spa_state_to_name(spa));
|
||||
|
||||
jp_printf(&jp, "state: %s", spa_state_to_name(spa));
|
||||
fnvlist_remove(spa_config, "state");
|
||||
spa_add_spares(spa, spa_config);
|
||||
spa_add_l2cache(spa, spa_config);
|
||||
spa_add_feature_stats(spa, spa_config);
|
||||
|
||||
spa_add_spares(spa, nvl);
|
||||
spa_add_l2cache(spa, nvl);
|
||||
spa_add_feature_stats(spa, nvl);
|
||||
|
||||
/* iterate and transfer nvl to json */
|
||||
nvlist_to_json(nvl, &jp, stats_filter);
|
||||
|
||||
iterate_vdevs(spa, &ps, &jp);
|
||||
|
||||
/*
|
||||
* close the root object
|
||||
*/
|
||||
jp_printf(&jp, "}");
|
||||
/* add spa_config to output nvlist */
|
||||
fnvlist_merge(nvl, spa_config);
|
||||
iterate_vdevs(spa, &ps, nvl);
|
||||
|
||||
if (scl_config_lock)
|
||||
spa_config_exit(spa, SCL_CONFIG, FTAG);
|
||||
nvlist_free(nvl);
|
||||
|
||||
error = jp_close(&jp);
|
||||
if (error == JPRINT_BUF_FULL) {
|
||||
error = SET_ERROR(ENOMEM);
|
||||
} else if (error != 0) {
|
||||
/*
|
||||
* Another error from jprint, format an error message
|
||||
* but this is not ever to happen (this would be a
|
||||
* defect elsewhere).
|
||||
*
|
||||
* If this does happen, we simply put the string where
|
||||
* the json should go... this is expected to trigger
|
||||
* a json decode error, and report "upstream"
|
||||
*/
|
||||
snprintf(buf, size,
|
||||
"jprint error %s (%d) callno %d, size %ld\n",
|
||||
jp_errorstring(error), error, jp_errorpos(&jp), size);
|
||||
error = 0;
|
||||
}
|
||||
error = nvlist_to_json(nvl, &curr, size);
|
||||
nvlist_free(nvl);
|
||||
nvlist_free(spa_config);
|
||||
nvlist_free(spa_props);
|
||||
nvlist_free(scan_stats);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue