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 <strings.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; 50 wchar_t c; 51 size_t sz; 52 53 bzero(&mbr, sizeof (mbr)); 54 55 FPRINTF(fp, "\""); 56 while ((sz = mbrtowc(&c, input, MB_CUR_MAX, &mbr)) > 0) { 57 if (sz == (size_t)-1 || sz == (size_t)-2) { 58 /* 59 * We last read an invalid multibyte character sequence, 60 * so return an error. 61 */ 62 return (-1); 63 } 64 switch (c) { 65 case '"': 66 FPRINTF(fp, "\\\""); 67 break; 68 case '\n': 69 FPRINTF(fp, "\\n"); 70 break; 71 case '\r': 72 FPRINTF(fp, "\\r"); 73 break; 74 case '\\': 75 FPRINTF(fp, "\\\\"); 76 break; 77 case '\f': 78 FPRINTF(fp, "\\f"); 79 break; 80 case '\t': 81 FPRINTF(fp, "\\t"); 82 break; 83 case '\b': 84 FPRINTF(fp, "\\b"); 85 break; 86 default: 87 if ((c >= 0x00 && c <= 0x1f) || 88 (c > 0x7f && c <= 0xffff)) { 89 /* 90 * Render both Control Characters and Unicode 91 * characters in the Basic Multilingual Plane 92 * as JSON-escaped multibyte characters. 93 */ 94 FPRINTF(fp, "\\u%04x", (int)(0xffff & c)); 95 } else if (c >= 0x20 && c <= 0x7f) { 96 /* 97 * Render other 7-bit ASCII characters directly 98 * and drop other, unrepresentable characters. 99 */ 100 FPRINTF(fp, "%c", (int)(0xff & c)); 101 } 102 break; 103 } 104 input += sz; 105 } 106 107 FPRINTF(fp, "\""); 108 return (0); 109 } 110 111 /* 112 * Dump a JSON-formatted representation of an nvlist to the provided FILE *. 113 * This routine does not output any new-lines or additional whitespace other 114 * than that contained in strings, nor does it call fflush(3C). 115 */ 116 int 117 nvlist_print_json(FILE *fp, nvlist_t *nvl) 118 { 119 nvpair_t *curr; 120 boolean_t first = B_TRUE; 121 122 FPRINTF(fp, "{"); 123 124 for (curr = nvlist_next_nvpair(nvl, NULL); curr; 125 curr = nvlist_next_nvpair(nvl, curr)) { 126 data_type_t type = nvpair_type(curr); 127 128 if (!first) 129 FPRINTF(fp, ","); 130 else 131 first = B_FALSE; 132 133 if (nvlist_print_json_string(fp, nvpair_name(curr)) == -1) 134 return (-1); 135 FPRINTF(fp, ":"); 136 137 switch (type) { 138 case DATA_TYPE_STRING: { 139 char *string = fnvpair_value_string(curr); 140 if (nvlist_print_json_string(fp, string) == -1) 141 return (-1); 142 break; 143 } 144 145 case DATA_TYPE_BOOLEAN: { 146 FPRINTF(fp, "true"); 147 break; 148 } 149 150 case DATA_TYPE_BOOLEAN_VALUE: { 151 FPRINTF(fp, "%s", fnvpair_value_boolean_value(curr) == 152 B_TRUE ? "true" : "false"); 153 break; 154 } 155 156 case DATA_TYPE_BYTE: { 157 FPRINTF(fp, "%hhu", fnvpair_value_byte(curr)); 158 break; 159 } 160 161 case DATA_TYPE_INT8: { 162 FPRINTF(fp, "%hhd", fnvpair_value_int8(curr)); 163 break; 164 } 165 166 case DATA_TYPE_UINT8: { 167 FPRINTF(fp, "%hhu", fnvpair_value_uint8(curr)); 168 break; 169 } 170 171 case DATA_TYPE_INT16: { 172 FPRINTF(fp, "%hd", fnvpair_value_int16(curr)); 173 break; 174 } 175 176 case DATA_TYPE_UINT16: { 177 FPRINTF(fp, "%hu", fnvpair_value_uint16(curr)); 178 break; 179 } 180 181 case DATA_TYPE_INT32: { 182 FPRINTF(fp, "%d", fnvpair_value_int32(curr)); 183 break; 184 } 185 186 case DATA_TYPE_UINT32: { 187 FPRINTF(fp, "%u", fnvpair_value_uint32(curr)); 188 break; 189 } 190 191 case DATA_TYPE_INT64: { 192 FPRINTF(fp, "%lld", 193 (long long)fnvpair_value_int64(curr)); 194 break; 195 } 196 197 case DATA_TYPE_UINT64: { 198 FPRINTF(fp, "%llu", 199 (unsigned long long)fnvpair_value_uint64(curr)); 200 break; 201 } 202 203 case DATA_TYPE_HRTIME: { 204 hrtime_t val; 205 VERIFY0(nvpair_value_hrtime(curr, &val)); 206 FPRINTF(fp, "%llu", (unsigned long long)val); 207 break; 208 } 209 210 case DATA_TYPE_DOUBLE: { 211 double val; 212 VERIFY0(nvpair_value_double(curr, &val)); 213 FPRINTF(fp, "%f", val); 214 break; 215 } 216 217 case DATA_TYPE_NVLIST: { 218 if (nvlist_print_json(fp, 219 fnvpair_value_nvlist(curr)) == -1) 220 return (-1); 221 break; 222 } 223 224 case DATA_TYPE_STRING_ARRAY: { 225 char **val; 226 uint_t valsz, i; 227 VERIFY0(nvpair_value_string_array(curr, &val, &valsz)); 228 FPRINTF(fp, "["); 229 for (i = 0; i < valsz; i++) { 230 if (i > 0) 231 FPRINTF(fp, ","); 232 if (nvlist_print_json_string(fp, val[i]) == -1) 233 return (-1); 234 } 235 FPRINTF(fp, "]"); 236 break; 237 } 238 239 case DATA_TYPE_NVLIST_ARRAY: { 240 nvlist_t **val; 241 uint_t valsz, i; 242 VERIFY0(nvpair_value_nvlist_array(curr, &val, &valsz)); 243 FPRINTF(fp, "["); 244 for (i = 0; i < valsz; i++) { 245 if (i > 0) 246 FPRINTF(fp, ","); 247 if (nvlist_print_json(fp, val[i]) == -1) 248 return (-1); 249 } 250 FPRINTF(fp, "]"); 251 break; 252 } 253 254 case DATA_TYPE_BOOLEAN_ARRAY: { 255 boolean_t *val; 256 uint_t valsz, i; 257 VERIFY0(nvpair_value_boolean_array(curr, &val, &valsz)); 258 FPRINTF(fp, "["); 259 for (i = 0; i < valsz; i++) { 260 if (i > 0) 261 FPRINTF(fp, ","); 262 FPRINTF(fp, val[i] == B_TRUE ? 263 "true" : "false"); 264 } 265 FPRINTF(fp, "]"); 266 break; 267 } 268 269 case DATA_TYPE_BYTE_ARRAY: { 270 uchar_t *val; 271 uint_t valsz, i; 272 VERIFY0(nvpair_value_byte_array(curr, &val, &valsz)); 273 FPRINTF(fp, "["); 274 for (i = 0; i < valsz; i++) { 275 if (i > 0) 276 FPRINTF(fp, ","); 277 FPRINTF(fp, "%hhu", val[i]); 278 } 279 FPRINTF(fp, "]"); 280 break; 281 } 282 283 case DATA_TYPE_UINT8_ARRAY: { 284 uint8_t *val; 285 uint_t valsz, i; 286 VERIFY0(nvpair_value_uint8_array(curr, &val, &valsz)); 287 FPRINTF(fp, "["); 288 for (i = 0; i < valsz; i++) { 289 if (i > 0) 290 FPRINTF(fp, ","); 291 FPRINTF(fp, "%hhu", val[i]); 292 } 293 FPRINTF(fp, "]"); 294 break; 295 } 296 297 case DATA_TYPE_INT8_ARRAY: { 298 int8_t *val; 299 uint_t valsz, i; 300 VERIFY0(nvpair_value_int8_array(curr, &val, &valsz)); 301 FPRINTF(fp, "["); 302 for (i = 0; i < valsz; i++) { 303 if (i > 0) 304 FPRINTF(fp, ","); 305 FPRINTF(fp, "%hhd", val[i]); 306 } 307 FPRINTF(fp, "]"); 308 break; 309 } 310 311 case DATA_TYPE_UINT16_ARRAY: { 312 uint16_t *val; 313 uint_t valsz, i; 314 VERIFY0(nvpair_value_uint16_array(curr, &val, &valsz)); 315 FPRINTF(fp, "["); 316 for (i = 0; i < valsz; i++) { 317 if (i > 0) 318 FPRINTF(fp, ","); 319 FPRINTF(fp, "%hu", val[i]); 320 } 321 FPRINTF(fp, "]"); 322 break; 323 } 324 325 case DATA_TYPE_INT16_ARRAY: { 326 int16_t *val; 327 uint_t valsz, i; 328 VERIFY0(nvpair_value_int16_array(curr, &val, &valsz)); 329 FPRINTF(fp, "["); 330 for (i = 0; i < valsz; i++) { 331 if (i > 0) 332 FPRINTF(fp, ","); 333 FPRINTF(fp, "%hd", val[i]); 334 } 335 FPRINTF(fp, "]"); 336 break; 337 } 338 339 case DATA_TYPE_UINT32_ARRAY: { 340 uint32_t *val; 341 uint_t valsz, i; 342 VERIFY0(nvpair_value_uint32_array(curr, &val, &valsz)); 343 FPRINTF(fp, "["); 344 for (i = 0; i < valsz; i++) { 345 if (i > 0) 346 FPRINTF(fp, ","); 347 FPRINTF(fp, "%u", val[i]); 348 } 349 FPRINTF(fp, "]"); 350 break; 351 } 352 353 case DATA_TYPE_INT32_ARRAY: { 354 int32_t *val; 355 uint_t valsz, i; 356 VERIFY0(nvpair_value_int32_array(curr, &val, &valsz)); 357 FPRINTF(fp, "["); 358 for (i = 0; i < valsz; i++) { 359 if (i > 0) 360 FPRINTF(fp, ","); 361 FPRINTF(fp, "%d", val[i]); 362 } 363 FPRINTF(fp, "]"); 364 break; 365 } 366 367 case DATA_TYPE_UINT64_ARRAY: { 368 uint64_t *val; 369 uint_t valsz, i; 370 VERIFY0(nvpair_value_uint64_array(curr, &val, &valsz)); 371 FPRINTF(fp, "["); 372 for (i = 0; i < valsz; i++) { 373 if (i > 0) 374 FPRINTF(fp, ","); 375 FPRINTF(fp, "%llu", 376 (unsigned long long)val[i]); 377 } 378 FPRINTF(fp, "]"); 379 break; 380 } 381 382 case DATA_TYPE_INT64_ARRAY: { 383 int64_t *val; 384 uint_t valsz, i; 385 VERIFY0(nvpair_value_int64_array(curr, &val, &valsz)); 386 FPRINTF(fp, "["); 387 for (i = 0; i < valsz; i++) { 388 if (i > 0) 389 FPRINTF(fp, ","); 390 FPRINTF(fp, "%lld", (long long)val[i]); 391 } 392 FPRINTF(fp, "]"); 393 break; 394 } 395 396 case DATA_TYPE_UNKNOWN: 397 case DATA_TYPE_DONTCARE: 398 return (-1); 399 } 400 401 } 402 403 FPRINTF(fp, "}"); 404 return (0); 405 } 406