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         if (fprintf(fp, __VA_ARGS__) < 0)    \
  25                 return (-1)                     \
  26 
  27 /*
  28  * When formatting a string for JSON output we must escape certain characters,
  29  * as described in RFC4627.  This applies to both member names and
  30  * DATA_TYPE_STRING values.
  31  *
  32  * This function will only operate correctly if the following conditions are
  33  * met:
  34  *
  35  *       1. The input String is encoded in the current locale.
  36  *
  37  *       2. The current locale includes the Basic Multilingual Plane (plane 0)
  38  *          as defined in the Unicode standard.
  39  *
  40  * The output will be entirely 7-bit ASCII (as a subset of UTF-8) with all
  41  * representable Unicode characters included in their escaped numeric form.
  42  */
  43 static int
  44 nvlist_print_json_string(FILE *fp, const char *input)
  45 {
  46         mbstate_t mbr;
  47         wchar_t c;
  48         size_t sz;
  49 
  50         bzero(&mbr, sizeof (mbr));
  51 
  52         FPRINTF(fp, "\"");
  53         while ((sz = mbrtowc(&c, input, MB_CUR_MAX, &mbr)) > 0) {
  54                 switch (c) {
  55                 case '"':
  56                         FPRINTF(fp, "\\\"");
  57                         break;
  58                 case '\n':
  59                         FPRINTF(fp, "\\n");
  60                         break;
  61                 case '\r':
  62                         FPRINTF(fp, "\\r");
  63                         break;
  64                 case '\\':
  65                         FPRINTF(fp, "\\\\");
  66                         break;
  67                 case '\f':
  68                         FPRINTF(fp, "\\f");
  69                         break;
  70                 case '\t':
  71                         FPRINTF(fp, "\\t");
  72                         break;
  73                 case '\b':
  74                         FPRINTF(fp, "\\b");
  75                         break;
  76                 default:
  77                         if ((c >= 0x00 && c <= 0x1f) ||
  78                             (c > 0x7f && c <= 0xffff)) {
  79                                 /*
  80                                  * Render both Control Characters and Unicode
  81                                  * characters in the Basic Multilingual Plane
  82                                  * as JSON-escaped multibyte characters.
  83                                  */
  84                                 FPRINTF(fp, "\\u%04x", (int)(0xffff & c));
  85                         } else if (c >= 0x20 && c <= 0x7f) {
  86                                 /*
  87                                  * Render other 7-bit ASCII characters directly
  88                                  * and drop other, unrepresentable characters.
  89                                  */
  90                                 FPRINTF(fp, "%c", (int)(0xff & c));
  91                         }
  92                         break;
  93                 }
  94                 input += sz;
  95         }
  96 
  97         if (sz == (size_t)-1 || sz == (size_t)-2) {
  98                 /*
  99                  * We last read an invalid multibyte character sequence,
 100                  * so return an error.
 101                  */
 102                 return (-1);
 103         }
 104 
 105         FPRINTF(fp, "\"");
 106         return (0);
 107 }
 108 
 109 /*
 110  * Dump a JSON-formatted representation of an nvlist to the provided FILE *.
 111  * This routine does not output any new-lines or additional whitespace other
 112  * than that contained in strings, nor does it call fflush(3C).
 113  */
 114 int
 115 nvlist_print_json(FILE *fp, nvlist_t *nvl)
 116 {
 117         nvpair_t *curr;
 118         boolean_t first = B_TRUE;
 119 
 120         FPRINTF(fp, "{");
 121 
 122         for (curr = nvlist_next_nvpair(nvl, NULL); curr;
 123             curr = nvlist_next_nvpair(nvl, curr)) {
 124                 data_type_t type = nvpair_type(curr);
 125 
 126                 if (!first)
 127                         FPRINTF(fp, ",");
 128                 else
 129                         first = B_FALSE;
 130 
 131                 if (nvlist_print_json_string(fp, nvpair_name(curr)) == -1)
 132                         return (-1);
 133                 FPRINTF(fp, ":");
 134 
 135                 switch (type) {
 136                 case DATA_TYPE_STRING: {
 137                         char *string = fnvpair_value_string(curr);
 138                         if (nvlist_print_json_string(fp, string) == -1)
 139                                 return (-1);
 140                         break;
 141                 }
 142 
 143                 case DATA_TYPE_BOOLEAN: {
 144                         FPRINTF(fp, "true");
 145                         break;
 146                 }
 147 
 148                 case DATA_TYPE_BOOLEAN_VALUE: {
 149                         FPRINTF(fp, "%s", fnvpair_value_boolean_value(curr) ==
 150                             B_TRUE ? "true" : "false");
 151                         break;
 152                 }
 153 
 154                 case DATA_TYPE_BYTE: {
 155                         FPRINTF(fp, "%hhu", fnvpair_value_byte(curr));
 156                         break;
 157                 }
 158 
 159                 case DATA_TYPE_INT8: {
 160                         FPRINTF(fp, "%hhd", fnvpair_value_int8(curr));
 161                         break;
 162                 }
 163 
 164                 case DATA_TYPE_UINT8: {
 165                         FPRINTF(fp, "%hhu", fnvpair_value_uint8_t(curr));
 166                         break;
 167                 }
 168 
 169                 case DATA_TYPE_INT16: {
 170                         FPRINTF(fp, "%hd", fnvpair_value_int16(curr));
 171                         break;
 172                 }
 173 
 174                 case DATA_TYPE_UINT16: {
 175                         FPRINTF(fp, "%hu", fnvpair_value_uint16(curr));
 176                         break;
 177                 }
 178 
 179                 case DATA_TYPE_INT32: {
 180                         FPRINTF(fp, "%d", fnvpair_value_int32(curr));
 181                         break;
 182                 }
 183 
 184                 case DATA_TYPE_UINT32: {
 185                         FPRINTF(fp, "%u", fnvpair_value_uint32(curr));
 186                         break;
 187                 }
 188 
 189                 case DATA_TYPE_INT64: {
 190                         FPRINTF(fp, "%lld",
 191                             (long long)fnvpair_value_int64(curr));
 192                         break;
 193                 }
 194 
 195                 case DATA_TYPE_UINT64: {
 196                         FPRINTF(fp, "%llu",
 197                             (unsigned long long)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", (unsigned long long)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",
 374                                     (unsigned long long)val[i]);
 375                         }
 376                         FPRINTF(fp, "]");
 377                         break;
 378                 }
 379 
 380                 case DATA_TYPE_INT64_ARRAY: {
 381                         int64_t *val;
 382                         uint_t valsz, i;
 383                         VERIFY0(nvpair_value_int64_array(curr, &val, &valsz));
 384                         FPRINTF(fp, "[");
 385                         for (i = 0; i < valsz; i++) {
 386                                 if (i > 0)
 387                                         FPRINTF(fp, ",");
 388                                 FPRINTF(fp, "%lld", (long long)val[i]);
 389                         }
 390                         FPRINTF(fp, "]");
 391                         break;
 392                 }
 393 
 394                 case DATA_TYPE_UNKNOWN:
 395                         return (-1);
 396                 }
 397         }
 398 
 399         FPRINTF(fp, "}");
 400         return (0);
 401 }