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