1eda14cbcSMatt Macy /* 2eda14cbcSMatt Macy * This file and its contents are supplied under the terms of the 3eda14cbcSMatt Macy * Common Development and Distribution License ("CDDL"), version 1.0. 4eda14cbcSMatt Macy * You may only use this file in accordance with the terms of version 5eda14cbcSMatt Macy * 1.0 of the CDDL. 6eda14cbcSMatt Macy * 7eda14cbcSMatt Macy * A full copy of the text of the CDDL should have accompanied this 8eda14cbcSMatt Macy * source. A copy of the CDDL is also available via the Internet at 9eda14cbcSMatt Macy * http://www.illumos.org/license/CDDL. 10eda14cbcSMatt Macy */ 11eda14cbcSMatt Macy /* 12eda14cbcSMatt Macy * Copyright (c) 2014, Joyent, Inc. 13eda14cbcSMatt Macy * Copyright (c) 2017 by Delphix. All rights reserved. 14eda14cbcSMatt Macy */ 15eda14cbcSMatt Macy 16eda14cbcSMatt Macy #include <stdio.h> 17eda14cbcSMatt Macy #include <stdlib.h> 18*da5137abSMartin Matuska #include <string.h> 19eda14cbcSMatt Macy #include <wchar.h> 20eda14cbcSMatt Macy #include <sys/debug.h> 21eda14cbcSMatt Macy 22eda14cbcSMatt Macy #include "libnvpair.h" 23eda14cbcSMatt Macy 24eda14cbcSMatt Macy #define FPRINTF(fp, ...) \ 25eda14cbcSMatt Macy do { \ 26eda14cbcSMatt Macy if (fprintf(fp, __VA_ARGS__) < 0) \ 27eda14cbcSMatt Macy return (-1); \ 28eda14cbcSMatt Macy } while (0) 29eda14cbcSMatt Macy 30eda14cbcSMatt Macy /* 31eda14cbcSMatt Macy * When formatting a string for JSON output we must escape certain characters, 32eda14cbcSMatt Macy * as described in RFC4627. This applies to both member names and 33eda14cbcSMatt Macy * DATA_TYPE_STRING values. 34eda14cbcSMatt Macy * 35eda14cbcSMatt Macy * This function will only operate correctly if the following conditions are 36eda14cbcSMatt Macy * met: 37eda14cbcSMatt Macy * 38eda14cbcSMatt Macy * 1. The input String is encoded in the current locale. 39eda14cbcSMatt Macy * 40eda14cbcSMatt Macy * 2. The current locale includes the Basic Multilingual Plane (plane 0) 41eda14cbcSMatt Macy * as defined in the Unicode standard. 42eda14cbcSMatt Macy * 43eda14cbcSMatt Macy * The output will be entirely 7-bit ASCII (as a subset of UTF-8) with all 44eda14cbcSMatt Macy * representable Unicode characters included in their escaped numeric form. 45eda14cbcSMatt Macy */ 46eda14cbcSMatt Macy static int 47eda14cbcSMatt Macy nvlist_print_json_string(FILE *fp, const char *input) 48eda14cbcSMatt Macy { 49*da5137abSMartin Matuska mbstate_t mbr = {0}; 50eda14cbcSMatt Macy wchar_t c; 51eda14cbcSMatt Macy size_t sz; 52eda14cbcSMatt Macy 53eda14cbcSMatt Macy FPRINTF(fp, "\""); 54eda14cbcSMatt Macy while ((sz = mbrtowc(&c, input, MB_CUR_MAX, &mbr)) > 0) { 5516038816SMartin Matuska if (sz == (size_t)-1 || sz == (size_t)-2) { 5616038816SMartin Matuska /* 5716038816SMartin Matuska * We last read an invalid multibyte character sequence, 5816038816SMartin Matuska * so return an error. 5916038816SMartin Matuska */ 6016038816SMartin Matuska return (-1); 6116038816SMartin Matuska } 62eda14cbcSMatt Macy switch (c) { 63eda14cbcSMatt Macy case '"': 64eda14cbcSMatt Macy FPRINTF(fp, "\\\""); 65eda14cbcSMatt Macy break; 66eda14cbcSMatt Macy case '\n': 67eda14cbcSMatt Macy FPRINTF(fp, "\\n"); 68eda14cbcSMatt Macy break; 69eda14cbcSMatt Macy case '\r': 70eda14cbcSMatt Macy FPRINTF(fp, "\\r"); 71eda14cbcSMatt Macy break; 72eda14cbcSMatt Macy case '\\': 73eda14cbcSMatt Macy FPRINTF(fp, "\\\\"); 74eda14cbcSMatt Macy break; 75eda14cbcSMatt Macy case '\f': 76eda14cbcSMatt Macy FPRINTF(fp, "\\f"); 77eda14cbcSMatt Macy break; 78eda14cbcSMatt Macy case '\t': 79eda14cbcSMatt Macy FPRINTF(fp, "\\t"); 80eda14cbcSMatt Macy break; 81eda14cbcSMatt Macy case '\b': 82eda14cbcSMatt Macy FPRINTF(fp, "\\b"); 83eda14cbcSMatt Macy break; 84eda14cbcSMatt Macy default: 85eda14cbcSMatt Macy if ((c >= 0x00 && c <= 0x1f) || 86eda14cbcSMatt Macy (c > 0x7f && c <= 0xffff)) { 87eda14cbcSMatt Macy /* 88eda14cbcSMatt Macy * Render both Control Characters and Unicode 89eda14cbcSMatt Macy * characters in the Basic Multilingual Plane 90eda14cbcSMatt Macy * as JSON-escaped multibyte characters. 91eda14cbcSMatt Macy */ 92eda14cbcSMatt Macy FPRINTF(fp, "\\u%04x", (int)(0xffff & c)); 93eda14cbcSMatt Macy } else if (c >= 0x20 && c <= 0x7f) { 94eda14cbcSMatt Macy /* 95eda14cbcSMatt Macy * Render other 7-bit ASCII characters directly 96eda14cbcSMatt Macy * and drop other, unrepresentable characters. 97eda14cbcSMatt Macy */ 98eda14cbcSMatt Macy FPRINTF(fp, "%c", (int)(0xff & c)); 99eda14cbcSMatt Macy } 100eda14cbcSMatt Macy break; 101eda14cbcSMatt Macy } 102eda14cbcSMatt Macy input += sz; 103eda14cbcSMatt Macy } 104eda14cbcSMatt Macy 105eda14cbcSMatt Macy FPRINTF(fp, "\""); 106eda14cbcSMatt Macy return (0); 107eda14cbcSMatt Macy } 108eda14cbcSMatt Macy 109eda14cbcSMatt Macy /* 110eda14cbcSMatt Macy * Dump a JSON-formatted representation of an nvlist to the provided FILE *. 111eda14cbcSMatt Macy * This routine does not output any new-lines or additional whitespace other 112eda14cbcSMatt Macy * than that contained in strings, nor does it call fflush(3C). 113eda14cbcSMatt Macy */ 114eda14cbcSMatt Macy int 115eda14cbcSMatt Macy nvlist_print_json(FILE *fp, nvlist_t *nvl) 116eda14cbcSMatt Macy { 117eda14cbcSMatt Macy nvpair_t *curr; 118eda14cbcSMatt Macy boolean_t first = B_TRUE; 119eda14cbcSMatt Macy 120eda14cbcSMatt Macy FPRINTF(fp, "{"); 121eda14cbcSMatt Macy 122eda14cbcSMatt Macy for (curr = nvlist_next_nvpair(nvl, NULL); curr; 123eda14cbcSMatt Macy curr = nvlist_next_nvpair(nvl, curr)) { 124eda14cbcSMatt Macy data_type_t type = nvpair_type(curr); 125eda14cbcSMatt Macy 126eda14cbcSMatt Macy if (!first) 127eda14cbcSMatt Macy FPRINTF(fp, ","); 128eda14cbcSMatt Macy else 129eda14cbcSMatt Macy first = B_FALSE; 130eda14cbcSMatt Macy 131eda14cbcSMatt Macy if (nvlist_print_json_string(fp, nvpair_name(curr)) == -1) 132eda14cbcSMatt Macy return (-1); 133eda14cbcSMatt Macy FPRINTF(fp, ":"); 134eda14cbcSMatt Macy 135eda14cbcSMatt Macy switch (type) { 136eda14cbcSMatt Macy case DATA_TYPE_STRING: { 137eda14cbcSMatt Macy char *string = fnvpair_value_string(curr); 138eda14cbcSMatt Macy if (nvlist_print_json_string(fp, string) == -1) 139eda14cbcSMatt Macy return (-1); 140eda14cbcSMatt Macy break; 141eda14cbcSMatt Macy } 142eda14cbcSMatt Macy 143eda14cbcSMatt Macy case DATA_TYPE_BOOLEAN: { 144eda14cbcSMatt Macy FPRINTF(fp, "true"); 145eda14cbcSMatt Macy break; 146eda14cbcSMatt Macy } 147eda14cbcSMatt Macy 148eda14cbcSMatt Macy case DATA_TYPE_BOOLEAN_VALUE: { 149eda14cbcSMatt Macy FPRINTF(fp, "%s", fnvpair_value_boolean_value(curr) == 150eda14cbcSMatt Macy B_TRUE ? "true" : "false"); 151eda14cbcSMatt Macy break; 152eda14cbcSMatt Macy } 153eda14cbcSMatt Macy 154eda14cbcSMatt Macy case DATA_TYPE_BYTE: { 155eda14cbcSMatt Macy FPRINTF(fp, "%hhu", fnvpair_value_byte(curr)); 156eda14cbcSMatt Macy break; 157eda14cbcSMatt Macy } 158eda14cbcSMatt Macy 159eda14cbcSMatt Macy case DATA_TYPE_INT8: { 160eda14cbcSMatt Macy FPRINTF(fp, "%hhd", fnvpair_value_int8(curr)); 161eda14cbcSMatt Macy break; 162eda14cbcSMatt Macy } 163eda14cbcSMatt Macy 164eda14cbcSMatt Macy case DATA_TYPE_UINT8: { 165eda14cbcSMatt Macy FPRINTF(fp, "%hhu", fnvpair_value_uint8(curr)); 166eda14cbcSMatt Macy break; 167eda14cbcSMatt Macy } 168eda14cbcSMatt Macy 169eda14cbcSMatt Macy case DATA_TYPE_INT16: { 170eda14cbcSMatt Macy FPRINTF(fp, "%hd", fnvpair_value_int16(curr)); 171eda14cbcSMatt Macy break; 172eda14cbcSMatt Macy } 173eda14cbcSMatt Macy 174eda14cbcSMatt Macy case DATA_TYPE_UINT16: { 175eda14cbcSMatt Macy FPRINTF(fp, "%hu", fnvpair_value_uint16(curr)); 176eda14cbcSMatt Macy break; 177eda14cbcSMatt Macy } 178eda14cbcSMatt Macy 179eda14cbcSMatt Macy case DATA_TYPE_INT32: { 180eda14cbcSMatt Macy FPRINTF(fp, "%d", fnvpair_value_int32(curr)); 181eda14cbcSMatt Macy break; 182eda14cbcSMatt Macy } 183eda14cbcSMatt Macy 184eda14cbcSMatt Macy case DATA_TYPE_UINT32: { 185eda14cbcSMatt Macy FPRINTF(fp, "%u", fnvpair_value_uint32(curr)); 186eda14cbcSMatt Macy break; 187eda14cbcSMatt Macy } 188eda14cbcSMatt Macy 189eda14cbcSMatt Macy case DATA_TYPE_INT64: { 190eda14cbcSMatt Macy FPRINTF(fp, "%lld", 191eda14cbcSMatt Macy (long long)fnvpair_value_int64(curr)); 192eda14cbcSMatt Macy break; 193eda14cbcSMatt Macy } 194eda14cbcSMatt Macy 195eda14cbcSMatt Macy case DATA_TYPE_UINT64: { 196eda14cbcSMatt Macy FPRINTF(fp, "%llu", 197eda14cbcSMatt Macy (unsigned long long)fnvpair_value_uint64(curr)); 198eda14cbcSMatt Macy break; 199eda14cbcSMatt Macy } 200eda14cbcSMatt Macy 201eda14cbcSMatt Macy case DATA_TYPE_HRTIME: { 202eda14cbcSMatt Macy hrtime_t val; 203eda14cbcSMatt Macy VERIFY0(nvpair_value_hrtime(curr, &val)); 204eda14cbcSMatt Macy FPRINTF(fp, "%llu", (unsigned long long)val); 205eda14cbcSMatt Macy break; 206eda14cbcSMatt Macy } 207eda14cbcSMatt Macy 208eda14cbcSMatt Macy case DATA_TYPE_DOUBLE: { 209eda14cbcSMatt Macy double val; 210eda14cbcSMatt Macy VERIFY0(nvpair_value_double(curr, &val)); 211eda14cbcSMatt Macy FPRINTF(fp, "%f", val); 212eda14cbcSMatt Macy break; 213eda14cbcSMatt Macy } 214eda14cbcSMatt Macy 215eda14cbcSMatt Macy case DATA_TYPE_NVLIST: { 216eda14cbcSMatt Macy if (nvlist_print_json(fp, 217eda14cbcSMatt Macy fnvpair_value_nvlist(curr)) == -1) 218eda14cbcSMatt Macy return (-1); 219eda14cbcSMatt Macy break; 220eda14cbcSMatt Macy } 221eda14cbcSMatt Macy 222eda14cbcSMatt Macy case DATA_TYPE_STRING_ARRAY: { 223eda14cbcSMatt Macy char **val; 224eda14cbcSMatt Macy uint_t valsz, i; 225eda14cbcSMatt Macy VERIFY0(nvpair_value_string_array(curr, &val, &valsz)); 226eda14cbcSMatt Macy FPRINTF(fp, "["); 227eda14cbcSMatt Macy for (i = 0; i < valsz; i++) { 228eda14cbcSMatt Macy if (i > 0) 229eda14cbcSMatt Macy FPRINTF(fp, ","); 230eda14cbcSMatt Macy if (nvlist_print_json_string(fp, val[i]) == -1) 231eda14cbcSMatt Macy return (-1); 232eda14cbcSMatt Macy } 233eda14cbcSMatt Macy FPRINTF(fp, "]"); 234eda14cbcSMatt Macy break; 235eda14cbcSMatt Macy } 236eda14cbcSMatt Macy 237eda14cbcSMatt Macy case DATA_TYPE_NVLIST_ARRAY: { 238eda14cbcSMatt Macy nvlist_t **val; 239eda14cbcSMatt Macy uint_t valsz, i; 240eda14cbcSMatt Macy VERIFY0(nvpair_value_nvlist_array(curr, &val, &valsz)); 241eda14cbcSMatt Macy FPRINTF(fp, "["); 242eda14cbcSMatt Macy for (i = 0; i < valsz; i++) { 243eda14cbcSMatt Macy if (i > 0) 244eda14cbcSMatt Macy FPRINTF(fp, ","); 245eda14cbcSMatt Macy if (nvlist_print_json(fp, val[i]) == -1) 246eda14cbcSMatt Macy return (-1); 247eda14cbcSMatt Macy } 248eda14cbcSMatt Macy FPRINTF(fp, "]"); 249eda14cbcSMatt Macy break; 250eda14cbcSMatt Macy } 251eda14cbcSMatt Macy 252eda14cbcSMatt Macy case DATA_TYPE_BOOLEAN_ARRAY: { 253eda14cbcSMatt Macy boolean_t *val; 254eda14cbcSMatt Macy uint_t valsz, i; 255eda14cbcSMatt Macy VERIFY0(nvpair_value_boolean_array(curr, &val, &valsz)); 256eda14cbcSMatt Macy FPRINTF(fp, "["); 257eda14cbcSMatt Macy for (i = 0; i < valsz; i++) { 258eda14cbcSMatt Macy if (i > 0) 259eda14cbcSMatt Macy FPRINTF(fp, ","); 260eda14cbcSMatt Macy FPRINTF(fp, val[i] == B_TRUE ? 261eda14cbcSMatt Macy "true" : "false"); 262eda14cbcSMatt Macy } 263eda14cbcSMatt Macy FPRINTF(fp, "]"); 264eda14cbcSMatt Macy break; 265eda14cbcSMatt Macy } 266eda14cbcSMatt Macy 267eda14cbcSMatt Macy case DATA_TYPE_BYTE_ARRAY: { 268eda14cbcSMatt Macy uchar_t *val; 269eda14cbcSMatt Macy uint_t valsz, i; 270eda14cbcSMatt Macy VERIFY0(nvpair_value_byte_array(curr, &val, &valsz)); 271eda14cbcSMatt Macy FPRINTF(fp, "["); 272eda14cbcSMatt Macy for (i = 0; i < valsz; i++) { 273eda14cbcSMatt Macy if (i > 0) 274eda14cbcSMatt Macy FPRINTF(fp, ","); 275eda14cbcSMatt Macy FPRINTF(fp, "%hhu", val[i]); 276eda14cbcSMatt Macy } 277eda14cbcSMatt Macy FPRINTF(fp, "]"); 278eda14cbcSMatt Macy break; 279eda14cbcSMatt Macy } 280eda14cbcSMatt Macy 281eda14cbcSMatt Macy case DATA_TYPE_UINT8_ARRAY: { 282eda14cbcSMatt Macy uint8_t *val; 283eda14cbcSMatt Macy uint_t valsz, i; 284eda14cbcSMatt Macy VERIFY0(nvpair_value_uint8_array(curr, &val, &valsz)); 285eda14cbcSMatt Macy FPRINTF(fp, "["); 286eda14cbcSMatt Macy for (i = 0; i < valsz; i++) { 287eda14cbcSMatt Macy if (i > 0) 288eda14cbcSMatt Macy FPRINTF(fp, ","); 289eda14cbcSMatt Macy FPRINTF(fp, "%hhu", val[i]); 290eda14cbcSMatt Macy } 291eda14cbcSMatt Macy FPRINTF(fp, "]"); 292eda14cbcSMatt Macy break; 293eda14cbcSMatt Macy } 294eda14cbcSMatt Macy 295eda14cbcSMatt Macy case DATA_TYPE_INT8_ARRAY: { 296eda14cbcSMatt Macy int8_t *val; 297eda14cbcSMatt Macy uint_t valsz, i; 298eda14cbcSMatt Macy VERIFY0(nvpair_value_int8_array(curr, &val, &valsz)); 299eda14cbcSMatt Macy FPRINTF(fp, "["); 300eda14cbcSMatt Macy for (i = 0; i < valsz; i++) { 301eda14cbcSMatt Macy if (i > 0) 302eda14cbcSMatt Macy FPRINTF(fp, ","); 303eda14cbcSMatt Macy FPRINTF(fp, "%hhd", val[i]); 304eda14cbcSMatt Macy } 305eda14cbcSMatt Macy FPRINTF(fp, "]"); 306eda14cbcSMatt Macy break; 307eda14cbcSMatt Macy } 308eda14cbcSMatt Macy 309eda14cbcSMatt Macy case DATA_TYPE_UINT16_ARRAY: { 310eda14cbcSMatt Macy uint16_t *val; 311eda14cbcSMatt Macy uint_t valsz, i; 312eda14cbcSMatt Macy VERIFY0(nvpair_value_uint16_array(curr, &val, &valsz)); 313eda14cbcSMatt Macy FPRINTF(fp, "["); 314eda14cbcSMatt Macy for (i = 0; i < valsz; i++) { 315eda14cbcSMatt Macy if (i > 0) 316eda14cbcSMatt Macy FPRINTF(fp, ","); 317eda14cbcSMatt Macy FPRINTF(fp, "%hu", val[i]); 318eda14cbcSMatt Macy } 319eda14cbcSMatt Macy FPRINTF(fp, "]"); 320eda14cbcSMatt Macy break; 321eda14cbcSMatt Macy } 322eda14cbcSMatt Macy 323eda14cbcSMatt Macy case DATA_TYPE_INT16_ARRAY: { 324eda14cbcSMatt Macy int16_t *val; 325eda14cbcSMatt Macy uint_t valsz, i; 326eda14cbcSMatt Macy VERIFY0(nvpair_value_int16_array(curr, &val, &valsz)); 327eda14cbcSMatt Macy FPRINTF(fp, "["); 328eda14cbcSMatt Macy for (i = 0; i < valsz; i++) { 329eda14cbcSMatt Macy if (i > 0) 330eda14cbcSMatt Macy FPRINTF(fp, ","); 331eda14cbcSMatt Macy FPRINTF(fp, "%hd", val[i]); 332eda14cbcSMatt Macy } 333eda14cbcSMatt Macy FPRINTF(fp, "]"); 334eda14cbcSMatt Macy break; 335eda14cbcSMatt Macy } 336eda14cbcSMatt Macy 337eda14cbcSMatt Macy case DATA_TYPE_UINT32_ARRAY: { 338eda14cbcSMatt Macy uint32_t *val; 339eda14cbcSMatt Macy uint_t valsz, i; 340eda14cbcSMatt Macy VERIFY0(nvpair_value_uint32_array(curr, &val, &valsz)); 341eda14cbcSMatt Macy FPRINTF(fp, "["); 342eda14cbcSMatt Macy for (i = 0; i < valsz; i++) { 343eda14cbcSMatt Macy if (i > 0) 344eda14cbcSMatt Macy FPRINTF(fp, ","); 345eda14cbcSMatt Macy FPRINTF(fp, "%u", val[i]); 346eda14cbcSMatt Macy } 347eda14cbcSMatt Macy FPRINTF(fp, "]"); 348eda14cbcSMatt Macy break; 349eda14cbcSMatt Macy } 350eda14cbcSMatt Macy 351eda14cbcSMatt Macy case DATA_TYPE_INT32_ARRAY: { 352eda14cbcSMatt Macy int32_t *val; 353eda14cbcSMatt Macy uint_t valsz, i; 354eda14cbcSMatt Macy VERIFY0(nvpair_value_int32_array(curr, &val, &valsz)); 355eda14cbcSMatt Macy FPRINTF(fp, "["); 356eda14cbcSMatt Macy for (i = 0; i < valsz; i++) { 357eda14cbcSMatt Macy if (i > 0) 358eda14cbcSMatt Macy FPRINTF(fp, ","); 359eda14cbcSMatt Macy FPRINTF(fp, "%d", val[i]); 360eda14cbcSMatt Macy } 361eda14cbcSMatt Macy FPRINTF(fp, "]"); 362eda14cbcSMatt Macy break; 363eda14cbcSMatt Macy } 364eda14cbcSMatt Macy 365eda14cbcSMatt Macy case DATA_TYPE_UINT64_ARRAY: { 366eda14cbcSMatt Macy uint64_t *val; 367eda14cbcSMatt Macy uint_t valsz, i; 368eda14cbcSMatt Macy VERIFY0(nvpair_value_uint64_array(curr, &val, &valsz)); 369eda14cbcSMatt Macy FPRINTF(fp, "["); 370eda14cbcSMatt Macy for (i = 0; i < valsz; i++) { 371eda14cbcSMatt Macy if (i > 0) 372eda14cbcSMatt Macy FPRINTF(fp, ","); 373eda14cbcSMatt Macy FPRINTF(fp, "%llu", 374eda14cbcSMatt Macy (unsigned long long)val[i]); 375eda14cbcSMatt Macy } 376eda14cbcSMatt Macy FPRINTF(fp, "]"); 377eda14cbcSMatt Macy break; 378eda14cbcSMatt Macy } 379eda14cbcSMatt Macy 380eda14cbcSMatt Macy case DATA_TYPE_INT64_ARRAY: { 381eda14cbcSMatt Macy int64_t *val; 382eda14cbcSMatt Macy uint_t valsz, i; 383eda14cbcSMatt Macy VERIFY0(nvpair_value_int64_array(curr, &val, &valsz)); 384eda14cbcSMatt Macy FPRINTF(fp, "["); 385eda14cbcSMatt Macy for (i = 0; i < valsz; i++) { 386eda14cbcSMatt Macy if (i > 0) 387eda14cbcSMatt Macy FPRINTF(fp, ","); 388eda14cbcSMatt Macy FPRINTF(fp, "%lld", (long long)val[i]); 389eda14cbcSMatt Macy } 390eda14cbcSMatt Macy FPRINTF(fp, "]"); 391eda14cbcSMatt Macy break; 392eda14cbcSMatt Macy } 393eda14cbcSMatt Macy 394eda14cbcSMatt Macy case DATA_TYPE_UNKNOWN: 395eda14cbcSMatt Macy case DATA_TYPE_DONTCARE: 396eda14cbcSMatt Macy return (-1); 397eda14cbcSMatt Macy } 398eda14cbcSMatt Macy 399eda14cbcSMatt Macy } 400eda14cbcSMatt Macy 401eda14cbcSMatt Macy FPRINTF(fp, "}"); 402eda14cbcSMatt Macy return (0); 403eda14cbcSMatt Macy } 404