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