xref: /illumos-gate/usr/src/lib/libnvpair/nvpair_json.c (revision 9b6c3dc02c8682cbff6680ba283839a0df30c2d0)
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) 2013, Joyent, Inc. All rights reserved.
13  */
14 
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <strings.h>
18 #include <wchar.h>
19 #include <sys/debug.h>
20 
21 #include "libnvpair.h"
22 
23 #define	FPRINTF(fp, ...)			\
24 	if (fprintf(fp, __VA_ARGS__) < 0)	\
25 		return (-1)			\
26 
27 /*
28  * When formatting a string for JSON output we must escape certain characters,
29  * as described in RFC4627.  This applies to both member names and
30  * DATA_TYPE_STRING values.
31  *
32  * This function will only operate correctly if the following conditions are
33  * met:
34  *
35  *       1. The input String is encoded in the current locale.
36  *
37  *       2. The current locale includes the Basic Multilingual Plane (plane 0)
38  *          as defined in the Unicode standard.
39  *
40  * The output will be entirely 7-bit ASCII (as a subset of UTF-8) with all
41  * representable Unicode characters included in their escaped numeric form.
42  */
43 static int
44 nvlist_print_json_string(FILE *fp, const char *input)
45 {
46 	mbstate_t mbr;
47 	wchar_t c;
48 	size_t sz;
49 
50 	bzero(&mbr, sizeof (mbr));
51 
52 	FPRINTF(fp, "\"");
53 	while ((sz = mbrtowc(&c, input, MB_CUR_MAX, &mbr)) > 0) {
54 		switch (c) {
55 		case '"':
56 			FPRINTF(fp, "\\\"");
57 			break;
58 		case '\n':
59 			FPRINTF(fp, "\\n");
60 			break;
61 		case '\r':
62 			FPRINTF(fp, "\\r");
63 			break;
64 		case '\\':
65 			FPRINTF(fp, "\\\\");
66 			break;
67 		case '\f':
68 			FPRINTF(fp, "\\f");
69 			break;
70 		case '\t':
71 			FPRINTF(fp, "\\t");
72 			break;
73 		case '\b':
74 			FPRINTF(fp, "\\b");
75 			break;
76 		default:
77 			if ((c >= 0x00 && c <= 0x1f) ||
78 			    (c > 0x7f && c <= 0xffff)) {
79 				/*
80 				 * Render both Control Characters and Unicode
81 				 * characters in the Basic Multilingual Plane
82 				 * as JSON-escaped multibyte characters.
83 				 */
84 				FPRINTF(fp, "\\u%04x", (int)(0xffff & c));
85 			} else if (c >= 0x20 && c <= 0x7f) {
86 				/*
87 				 * Render other 7-bit ASCII characters directly
88 				 * and drop other, unrepresentable characters.
89 				 */
90 				FPRINTF(fp, "%c", (int)(0xff & c));
91 			}
92 			break;
93 		}
94 		input += sz;
95 	}
96 
97 	if (sz == (size_t)-1 || sz == (size_t)-2) {
98 		/*
99 		 * We last read an invalid multibyte character sequence,
100 		 * so return an error.
101 		 */
102 		return (-1);
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_t(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, "%hd", 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, "%hhd", 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 			return (-1);
396 		}
397 	}
398 
399 	FPRINTF(fp, "}");
400 	return (0);
401 }
402