xref: /illumos-gate/usr/src/lib/libnvpair/nvpair_json.c (revision e760f15095bdc9fa107e7c20ed2a5e4fb5865c1d)
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 		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
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_t(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