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