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. 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", (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  */
 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",
 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, "%hd", 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 }