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); }