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