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         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  */
  45 static int
  46 nvlist_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", 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", 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  */
 116 int
 117 nvlist_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", fnvpair_value_int64(curr));
 193                         break;
 194                 }
 195 
 196                 case DATA_TYPE_UINT64: {
 197                         FPRINTF(fp, "%llu", 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", 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", val[i]);
 374                         }
 375                         FPRINTF(fp, "]");
 376                         break;
 377                 }
 378 
 379                 case DATA_TYPE_INT64_ARRAY: {
 380                         int64_t *val;
 381                         uint_t valsz, i;
 382                         VERIFY0(nvpair_value_int64_array(curr, &val, &valsz));
 383                         FPRINTF(fp, "[");
 384                         for (i = 0; i < valsz; i++) {
 385                                 if (i > 0)
 386                                         FPRINTF(fp, ",");
 387                                 FPRINTF(fp, "%lld", val[i]);
 388                         }
 389                         FPRINTF(fp, "]");
 390                         break;
 391                 }
 392 
 393                 case DATA_TYPE_UNKNOWN:
 394                         return (-1);
 395                 }
 396         }
 397 
 398         FPRINTF(fp, "}");
 399         return (0);
 400 }