diff --git a/usr/src/lib/libnvpair/libnvpair.h b/usr/src/lib/libnvpair/libnvpair.h index 197ec37f4624..bb8bfe1a2230 100644 --- a/usr/src/lib/libnvpair/libnvpair.h +++ b/usr/src/lib/libnvpair/libnvpair.h @@ -20,7 +20,7 @@ */ /* * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2013, Joyent, Inc. All rights reserved. + * Copyright (c) 2017, Joyent, Inc. */ #ifndef _LIBNVPAIR_H @@ -48,8 +48,10 @@ extern int nvpair_value_match_regex(nvpair_t *, int, char *, regex_t *, extern void nvlist_print(FILE *, nvlist_t *); extern int nvlist_print_json(FILE *, nvlist_t *); +extern int nvlist_print_json_pretty(FILE *, nvlist_t *, char *); extern void dump_nvlist(nvlist_t *, int); extern int nvlist_dump_json(nvlist_t *, char **); +extern int nvlist_dump_json_pretty(nvlist_t *, char **, char *); extern void nvlist_dump_json_free(nvlist_t *, char *); /* diff --git a/usr/src/lib/libnvpair/mapfile-vers b/usr/src/lib/libnvpair/mapfile-vers index 9b1f048f759c..d1553e61d5cf 100644 --- a/usr/src/lib/libnvpair/mapfile-vers +++ b/usr/src/lib/libnvpair/mapfile-vers @@ -21,7 +21,7 @@ # # Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. # Copyright (c) 2012 by Delphix. All rights reserved. -# Copyright (c) 2013, Joyent, Inc. All rights reserved. +# Copyright (c) 2017, Joyent, Inc. # # @@ -245,9 +245,11 @@ SYMBOL_VERSION SUNWprivate_1.1 { nvlist_add_hrtime; nvlist_lookup_hrtime; nvlist_dump_json; + nvlist_dump_json_pretty; nvlist_dump_json_free; nvlist_print; nvlist_print_json; + nvlist_print_json_pretty; nvlist_prt; nvlist_prtctl_alloc; nvlist_prtctl_free; diff --git a/usr/src/lib/libnvpair/nvpair_json.c b/usr/src/lib/libnvpair/nvpair_json.c index 7ebd1be7a022..559010bd51f4 100644 --- a/usr/src/lib/libnvpair/nvpair_json.c +++ b/usr/src/lib/libnvpair/nvpair_json.c @@ -9,7 +9,7 @@ * http://www.illumos.org/license/CDDL. */ /* - * Copyright (c) 2014, Joyent, Inc. + * Copyright (c) 2017, Joyent, Inc. */ #include @@ -29,6 +29,35 @@ return (-1); \ } while (0) +#define FPRINTF_JSON_ARRAY(val, valsz, func, level, indent, bufp, \ + blen, offp) \ + do { \ + int i, j; \ + FPRINTF(bufp, blen, offp, "["); \ + for (i = 0; i < valsz; i++) { \ + if (i > 0) { \ + FPRINTF(bufp, blen, offp, ","); \ + } \ + if (indent != NULL) { \ + FPRINTF(bufp, blen, offp, "\n"); \ + for (j = 0; j <= level + 1; j++) \ + FPRINTF(bufp, blen, offp, \ + "%s", indent); \ + } \ + if (func(val[i], bufp, blen, offp) == -1) { \ + return (-1); \ + } \ + } \ + if (indent != NULL) { \ + FPRINTF(bufp, blen, offp, "\n"); \ + for (j = 0; j <= level; j++) { \ + FPRINTF(bufp, blen, offp, "%s", \ + indent); \ + } \ + } \ + FPRINTF(bufp, blen, offp, "]"); \ + } while (0) + /* * A realloc-aware snprintf/asprintf like function. */ @@ -167,7 +196,93 @@ nvlist_print_json_string(const char *input, char **bufp, size_t *blen, } static int -nvlist_do_json(nvlist_t *nvl, char **bufp, size_t *blen, off_t *offp) +nvlist_print_json_boolean_value(boolean_t b, char **bufp, size_t *blen, + off_t *offp) +{ + FPRINTF(bufp, blen, offp, "%s", b == B_TRUE ? "true" : "false"); + return (0); +} + +static int +nvlist_print_json_byte(char byte, char **bufp, size_t *blen, off_t *offp) +{ + FPRINTF(bufp, blen, offp, "%hhu", byte); + return (0); +} + +static int +nvlist_print_json_int8(int8_t num, char **bufp, size_t *blen, off_t *offp) +{ + FPRINTF(bufp, blen, offp, "%hhd", num); + return (0); +} + +static int +nvlist_print_json_uint8(uint8_t num, char **bufp, size_t *blen, off_t *offp) +{ + FPRINTF(bufp, blen, offp, "%hhu", num); + return (0); +} + +static int +nvlist_print_json_int16(int16_t num, char **bufp, size_t *blen, off_t *offp) +{ + FPRINTF(bufp, blen, offp, "%hd", num); + return (0); +} + +static int +nvlist_print_json_uint16(uint16_t num, char **bufp, size_t *blen, off_t *offp) +{ + FPRINTF(bufp, blen, offp, "%hu", num); + return (0); +} + +static int +nvlist_print_json_int32(int32_t num, char **bufp, size_t *blen, off_t *offp) +{ + FPRINTF(bufp, blen, offp, "%d", num); + return (0); +} + +static int +nvlist_print_json_uint32(uint32_t num, char **bufp, size_t *blen, off_t *offp) +{ + FPRINTF(bufp, blen, offp, "%u", num); + return (0); +} + +static int +nvlist_print_json_int64(int64_t num, char **bufp, size_t *blen, off_t *offp) +{ + FPRINTF(bufp, blen, offp, "%lld", (long long)num); + return (0); +} + +static int +nvlist_print_json_uint64(uint64_t num, char **bufp, size_t *blen, off_t *offp) +{ + FPRINTF(bufp, blen, offp, "%llu", (unsigned long long)num); + return (0); +} + +static int +nvlist_print_json_hrtime(hrtime_t val, char **bufp, size_t *blen, off_t *offp) +{ + FPRINTF(bufp, blen, offp, "%llu", (unsigned long long)val); + return (0); +} + +static int +nvlist_print_json_double(double num, char **bufp, size_t *blen, off_t *offp) +{ + FPRINTF(bufp, blen, offp, "%f", num); + return (0); +} + +static int +nvlist_do_json(nvlist_t *nvl, char **bufp, size_t *blen, off_t *offp, + char *indent, int level) { nvpair_t *curr; boolean_t first = B_TRUE; @@ -176,6 +291,7 @@ nvlist_do_json(nvlist_t *nvl, char **bufp, size_t *blen, off_t *offp) for (curr = nvlist_next_nvpair(nvl, NULL); curr; curr = nvlist_next_nvpair(nvl, curr)) { + int i; data_type_t type = nvpair_type(curr); if (!first) @@ -183,121 +299,138 @@ nvlist_do_json(nvlist_t *nvl, char **bufp, size_t *blen, off_t *offp) else first = B_FALSE; + if (indent != NULL) { + FPRINTF(bufp, blen, offp, "\n"); + for (i = 0; i <= level; i++) { + FPRINTF(bufp, blen, offp, "%s", indent); + } + } + if (nvlist_print_json_string(nvpair_name(curr), bufp, blen, offp) == -1) return (-1); - FPRINTF(bufp, blen, offp, ":"); + FPRINTF(bufp, blen, offp, indent == NULL ? ":" : ": "); switch (type) { case DATA_TYPE_STRING: { - char *string = fnvpair_value_string(curr); - if (nvlist_print_json_string(string, bufp, blen, - offp) == -1) + if (nvlist_print_json_string( + fnvpair_value_string(curr), bufp, blen, offp) != 0) return (-1); break; } case DATA_TYPE_BOOLEAN: { - FPRINTF(bufp, blen, offp, "true"); + if (nvlist_print_json_boolean_value(B_TRUE, bufp, blen, + offp) != 0) + return (-1); break; } case DATA_TYPE_BOOLEAN_VALUE: { - FPRINTF(bufp, blen, offp, "%s", - fnvpair_value_boolean_value(curr) == B_TRUE ? - "true" : "false"); + if (nvlist_print_json_boolean_value( + fnvpair_value_boolean_value(curr), bufp, blen, + offp) != 0) + return (-1); break; } case DATA_TYPE_BYTE: { - FPRINTF(bufp, blen, offp, "%hhu", - fnvpair_value_byte(curr)); + if (nvlist_print_json_byte( + fnvpair_value_byte(curr), bufp, blen, offp) != 0) + return (-1); break; } case DATA_TYPE_INT8: { - FPRINTF(bufp, blen, offp, "%hhd", - fnvpair_value_int8(curr)); + if (nvlist_print_json_int8( + fnvpair_value_int8(curr), bufp, blen, offp) != 0) + return (-1); break; } case DATA_TYPE_UINT8: { - FPRINTF(bufp, blen, offp, "%hhu", - fnvpair_value_uint8_t(curr)); + if (nvlist_print_json_uint8( + fnvpair_value_uint8_t(curr), bufp, blen, + offp) != 0) + return (-1); break; } case DATA_TYPE_INT16: { - FPRINTF(bufp, blen, offp, "%hd", - fnvpair_value_int16(curr)); + if (nvlist_print_json_int16( + fnvpair_value_int16(curr), bufp, blen, offp) != 0) + return (-1); break; } case DATA_TYPE_UINT16: { - FPRINTF(bufp, blen, offp, "%hu", - fnvpair_value_uint16(curr)); + if (nvlist_print_json_uint16( + fnvpair_value_uint16(curr), bufp, blen, offp) != 0) + return (-1); break; } case DATA_TYPE_INT32: { - FPRINTF(bufp, blen, offp, "%d", - fnvpair_value_int32(curr)); + if (nvlist_print_json_int32( + fnvpair_value_int32(curr), bufp, blen, offp) != 0) + return (-1); break; } case DATA_TYPE_UINT32: { - FPRINTF(bufp, blen, offp, "%u", - fnvpair_value_uint32(curr)); + if (nvlist_print_json_uint32( + fnvpair_value_uint32(curr), bufp, blen, offp) != 0) + return (-1); break; } case DATA_TYPE_INT64: { - FPRINTF(bufp, blen, offp, "%lld", - (long long)fnvpair_value_int64(curr)); + if (nvlist_print_json_int64( + fnvpair_value_int64(curr), bufp, blen, offp) != 0) + return (-1); break; } case DATA_TYPE_UINT64: { - FPRINTF(bufp, blen, offp, "%llu", - (unsigned long long)fnvpair_value_uint64(curr)); + if (nvlist_print_json_uint64( + fnvpair_value_uint64(curr), bufp, blen, offp) != 0) + return (-1); break; } case DATA_TYPE_HRTIME: { hrtime_t val; VERIFY0(nvpair_value_hrtime(curr, &val)); - FPRINTF(bufp, blen, offp, "%llu", - (unsigned long long)val); + + if (nvlist_print_json_hrtime(val, bufp, blen, + offp) != 0) + return (-1); break; } case DATA_TYPE_DOUBLE: { double val; VERIFY0(nvpair_value_double(curr, &val)); - FPRINTF(bufp, blen, offp, "%f", val); + + if (nvlist_print_json_double( + val, bufp, blen, offp) != 0) + return (-1); break; } case DATA_TYPE_NVLIST: { if (nvlist_do_json(fnvpair_value_nvlist(curr), bufp, - blen, offp) == -1) + blen, offp, indent, level + 1) == -1) return (-1); break; } case DATA_TYPE_STRING_ARRAY: { char **val; - uint_t valsz, i; + uint_t valsz; VERIFY0(nvpair_value_string_array(curr, &val, &valsz)); - FPRINTF(bufp, blen, offp, "["); - for (i = 0; i < valsz; i++) { - if (i > 0) - FPRINTF(bufp, blen, offp, ","); - if (nvlist_print_json_string(val[i], bufp, - blen, offp) == -1) - return (-1); - } - FPRINTF(bufp, blen, offp, "]"); + FPRINTF_JSON_ARRAY(val, valsz, nvlist_print_json_string, + level, indent, bufp, blen, offp); break; } @@ -310,7 +443,7 @@ nvlist_do_json(nvlist_t *nvl, char **bufp, size_t *blen, off_t *offp) if (i > 0) FPRINTF(bufp, blen, offp, ","); if (nvlist_do_json(val[i], bufp, blen, - offp) == -1) + offp, indent, level + 1) == -1) return (-1); } FPRINTF(bufp, blen, offp, "]"); @@ -319,144 +452,92 @@ nvlist_do_json(nvlist_t *nvl, char **bufp, size_t *blen, off_t *offp) case DATA_TYPE_BOOLEAN_ARRAY: { boolean_t *val; - uint_t valsz, i; + uint_t valsz; VERIFY0(nvpair_value_boolean_array(curr, &val, &valsz)); - FPRINTF(bufp, blen, offp, "["); - for (i = 0; i < valsz; i++) { - if (i > 0) - FPRINTF(bufp, blen, offp, ","); - FPRINTF(bufp, blen, offp, val[i] == B_TRUE ? - "true" : "false"); - } - FPRINTF(bufp, blen, offp, "]"); + FPRINTF_JSON_ARRAY(val, valsz, + nvlist_print_json_boolean_value, level, indent, + bufp, blen, offp); break; } case DATA_TYPE_BYTE_ARRAY: { uchar_t *val; - uint_t valsz, i; + uint_t valsz; VERIFY0(nvpair_value_byte_array(curr, &val, &valsz)); - FPRINTF(bufp, blen, offp, "["); - for (i = 0; i < valsz; i++) { - if (i > 0) - FPRINTF(bufp, blen, offp, ","); - FPRINTF(bufp, blen, offp, "%hhu", val[i]); - } - FPRINTF(bufp, blen, offp, "]"); + FPRINTF_JSON_ARRAY(val, valsz, nvlist_print_json_byte, + level, indent, bufp, blen, offp); break; } case DATA_TYPE_UINT8_ARRAY: { uint8_t *val; - uint_t valsz, i; + uint_t valsz; VERIFY0(nvpair_value_uint8_array(curr, &val, &valsz)); - FPRINTF(bufp, blen, offp, "["); - for (i = 0; i < valsz; i++) { - if (i > 0) - FPRINTF(bufp, blen, offp, ","); - FPRINTF(bufp, blen, offp, "%hhu", val[i]); - } - FPRINTF(bufp, blen, offp, "]"); + FPRINTF_JSON_ARRAY(val, valsz, nvlist_print_json_uint8, + level, indent, bufp, blen, offp); break; } case DATA_TYPE_INT8_ARRAY: { int8_t *val; - uint_t valsz, i; + uint_t valsz; VERIFY0(nvpair_value_int8_array(curr, &val, &valsz)); - FPRINTF(bufp, blen, offp, "["); - for (i = 0; i < valsz; i++) { - if (i > 0) - FPRINTF(bufp, blen, offp, ","); - FPRINTF(bufp, blen, offp, "%hd", val[i]); - } - FPRINTF(bufp, blen, offp, "]"); + FPRINTF_JSON_ARRAY(val, valsz, nvlist_print_json_int8, + level, indent, bufp, blen, offp); break; } case DATA_TYPE_UINT16_ARRAY: { uint16_t *val; - uint_t valsz, i; + uint_t valsz; VERIFY0(nvpair_value_uint16_array(curr, &val, &valsz)); - FPRINTF(bufp, blen, offp, "["); - for (i = 0; i < valsz; i++) { - if (i > 0) - FPRINTF(bufp, blen, offp, ","); - FPRINTF(bufp, blen, offp, "%hu", val[i]); - } - FPRINTF(bufp, blen, offp, "]"); + FPRINTF_JSON_ARRAY(val, valsz, nvlist_print_json_uint16, + level, indent, bufp, blen, offp); break; } case DATA_TYPE_INT16_ARRAY: { int16_t *val; - uint_t valsz, i; + uint_t valsz; VERIFY0(nvpair_value_int16_array(curr, &val, &valsz)); - FPRINTF(bufp, blen, offp, "["); - for (i = 0; i < valsz; i++) { - if (i > 0) - FPRINTF(bufp, blen, offp, ","); - FPRINTF(bufp, blen, offp, "%hd", val[i]); - } - FPRINTF(bufp, blen, offp, "]"); + FPRINTF_JSON_ARRAY(val, valsz, nvlist_print_json_int16, + level, indent, bufp, blen, offp); break; } case DATA_TYPE_UINT32_ARRAY: { uint32_t *val; - uint_t valsz, i; + uint_t valsz; VERIFY0(nvpair_value_uint32_array(curr, &val, &valsz)); - FPRINTF(bufp, blen, offp, "["); - for (i = 0; i < valsz; i++) { - if (i > 0) - FPRINTF(bufp, blen, offp, ","); - FPRINTF(bufp, blen, offp, "%u", val[i]); - } - FPRINTF(bufp, blen, offp, "]"); + FPRINTF_JSON_ARRAY(val, valsz, nvlist_print_json_uint32, + level, indent, bufp, blen, offp); break; } case DATA_TYPE_INT32_ARRAY: { int32_t *val; - uint_t valsz, i; + uint_t valsz; VERIFY0(nvpair_value_int32_array(curr, &val, &valsz)); - FPRINTF(bufp, blen, offp, "["); - for (i = 0; i < valsz; i++) { - if (i > 0) - FPRINTF(bufp, blen, offp, ","); - FPRINTF(bufp, blen, offp, "%d", val[i]); - } - FPRINTF(bufp, blen, offp, "]"); + FPRINTF_JSON_ARRAY(val, valsz, nvlist_print_json_int32, + level, indent, bufp, blen, offp); break; } case DATA_TYPE_UINT64_ARRAY: { uint64_t *val; - uint_t valsz, i; + uint_t valsz; VERIFY0(nvpair_value_uint64_array(curr, &val, &valsz)); - FPRINTF(bufp, blen, offp, "["); - for (i = 0; i < valsz; i++) { - if (i > 0) - FPRINTF(bufp, blen, offp, ","); - FPRINTF(bufp, blen, offp, "%llu", - (unsigned long long)val[i]); - } - FPRINTF(bufp, blen, offp, "]"); + FPRINTF_JSON_ARRAY(val, valsz, nvlist_print_json_uint64, + level, indent, bufp, blen, offp); break; } case DATA_TYPE_INT64_ARRAY: { int64_t *val; - uint_t valsz, i; + uint_t valsz; VERIFY0(nvpair_value_int64_array(curr, &val, &valsz)); - FPRINTF(bufp, blen, offp, "["); - for (i = 0; i < valsz; i++) { - if (i > 0) - FPRINTF(bufp, blen, offp, ","); - FPRINTF(bufp, blen, offp, "%lld", - (long long)val[i]); - } - FPRINTF(bufp, blen, offp, "]"); + FPRINTF_JSON_ARRAY(val, valsz, nvlist_print_json_int64, + level, indent, bufp, blen, offp); break; } @@ -465,18 +546,33 @@ nvlist_do_json(nvlist_t *nvl, char **bufp, size_t *blen, off_t *offp) } } + if (indent != NULL) { + FPRINTF(bufp, blen, offp, "\n"); + } + FPRINTF(bufp, blen, offp, "}"); + + if (indent != NULL) { + FPRINTF(bufp, blen, offp, "\n"); + } + return (0); } int nvlist_dump_json(nvlist_t *nvl, char **bufp) +{ + return (nvlist_dump_json_pretty(nvl, bufp, NULL)); +} + +int +nvlist_dump_json_pretty(nvlist_t *nvl, char **bufp, char *indent) { off_t off = 0; size_t l = 0; *bufp = NULL; - return (nvlist_do_json(nvl, bufp, &l, &off)); + return (nvlist_do_json(nvl, bufp, &l, &off, indent, 0)); } /* ARGSUSED */ @@ -493,11 +589,25 @@ nvlist_dump_json_free(nvlist_t *nvl, char *buf) */ int nvlist_print_json(FILE *fp, nvlist_t *nvl) +{ + return (nvlist_print_json_pretty(fp, nvl, NULL)); +} + +/* + * Dump a JSON-formatted representation of an nvlist to the provided FILE *. + * This routine outputs newlines and extra whitespace to make the output + * "pretty". The string given in `indent` will be used to indent the + * output. For example, give " " for 2 space indentation, " " for 4 + * space indentation, "\t" for tabbed indentation, etc. An indent of NULL + * will disable pretty-printing. + */ +int +nvlist_print_json_pretty(FILE *fp, nvlist_t *nvl, char *indent) { int ret; char *buf; - if ((ret = nvlist_dump_json(nvl, &buf)) < 0) + if ((ret = nvlist_dump_json_pretty(nvl, &buf, indent)) < 0) return (ret); ret = fprintf(fp, "%s", buf); nvlist_dump_json_free(nvl, buf); diff --git a/usr/src/test/util-tests/tests/libnvpair_json/json_09_pretty.ksh b/usr/src/test/util-tests/tests/libnvpair_json/json_09_pretty.ksh new file mode 100644 index 000000000000..82e257007d4c --- /dev/null +++ b/usr/src/test/util-tests/tests/libnvpair_json/json_09_pretty.ksh @@ -0,0 +1,34 @@ +#!/bin/ksh +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2017, Joyent, Inc. +# + +DIR=$(dirname $(whence $0)) +. ${DIR}/json_common + +BASELINE="$(cat <= 2 && strcmp(argv[1], "pretty") == 0) + is_pretty = B_TRUE; + /* * Be locale-aware. The JSON output functions will process multibyte * characters in the current locale, and emit a correct JSON encoding @@ -814,9 +821,15 @@ main(int argc, char **argv) /* * Print the resultant list, and a terminating newline: */ - if (nvlist_print_json(stdout, lw->lw_nvl[0]) != 0 || - fprintf(stdout, "\n") < 0) - goto out; + if (is_pretty) { + if (nvlist_print_json_pretty(stdout, lw->lw_nvl[0], " ") < 0) + goto out; + } else { + if (nvlist_print_json(stdout, lw->lw_nvl[0]) < 0 || + fprintf(stdout, "\n") < 0) { + goto out; + } + } rc = EXIT_SUCCESS;