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