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