diff --git a/include/Makefile.am b/include/Makefile.am
index fcd0e51c72..504aa8592f 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -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 \
diff --git a/include/sys/jprint.h b/include/sys/jprint.h
deleted file mode 100644
index fef1a79642..0000000000
--- a/include/sys/jprint.h
+++ /dev/null
@@ -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 */
diff --git a/include/sys/nvpair.h b/include/sys/nvpair.h
index 2dbd9e3eaf..e22c5b5979 100644
--- a/include/sys/nvpair.h
+++ b/include/sys/nvpair.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 *);
diff --git a/lib/libnvpair/libnvpair.abi b/lib/libnvpair/libnvpair.abi
index 69009375e8..4126c379d2 100644
--- a/lib/libnvpair/libnvpair.abi
+++ b/lib/libnvpair/libnvpair.abi
@@ -117,6 +117,7 @@
+
@@ -2780,6 +2781,12 @@
+
+
+
+
+
+
diff --git a/lib/libzpool/Makefile.am b/lib/libzpool/Makefile.am
index b114fb3303..ab57d13974 100644
--- a/lib/libzpool/Makefile.am
+++ b/lib/libzpool/Makefile.am
@@ -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 \
diff --git a/module/Kbuild.in b/module/Kbuild.in
index 7d7acfb463..5b65d85272 100644
--- a/module/Kbuild.in
+++ b/module/Kbuild.in
@@ -353,7 +353,6 @@ ZFS_OBJS := \
fm.o \
gzip.o \
hkdf.o \
- jprint.o \
lz4.o \
lz4_zfs.o \
lzjb.o \
diff --git a/module/Makefile.bsd b/module/Makefile.bsd
index 971ada5a58..f281de92d3 100644
--- a/module/Makefile.bsd
+++ b/module/Makefile.bsd
@@ -282,7 +282,6 @@ SRCS+= abd.c \
edonr_zfs.c \
fm.c \
gzip.c \
- jprint.c \
lz4.c \
lz4_zfs.c \
lzjb.c \
diff --git a/module/nvpair/nvpair.c b/module/nvpair/nvpair.c
index 887f7d32df..af64d5aa92 100644
--- a/module/nvpair/nvpair.c
+++ b/module/nvpair/nvpair.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.
*
diff --git a/module/zfs/jprint.c b/module/zfs/jprint.c
deleted file mode 100644
index 7070423aed..0000000000
--- a/module/zfs/jprint.c
+++ /dev/null
@@ -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
-#include
-
-/* 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));
-}
diff --git a/module/zfs/spa_json_stats.c b/module/zfs/spa_json_stats.c
index 21c13f1a83..51700484ac 100644
--- a/module/zfs/spa_json_stats.c
+++ b/module/zfs/spa_json_stats.c
@@ -28,284 +28,11 @@
#include
#include
#include
-#include
#include
#include
#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);
}