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