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(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                         FPRINTF(fp, "%llu", fnvpair_value_hrtime(curr));
 203                         break;
 204                 }
 205 
 206                 case DATA_TYPE_DOUBLE: {
 207                         FPRINTF(fp, "%f", fnvpair_value_double(curr));
 208                         break;
 209                 }
 210 
 211                 case DATA_TYPE_NVLIST: {
 212                         if (nvlist_print_json(fp,
 213                             fnvpair_value_nvlist(curr)) == -1)
 214                                 return (-1);
 215                         break;
 216                 }
 217 
 218                 case DATA_TYPE_STRING_ARRAY: {
 219                         char **val;
 220                         uint_t valsz, i;
 221                         VERIFY0(nvpair_value_string_array(curr, &val, &valsz));
 222                         FPRINTF(fp, "[");
 223                         for (i = 0; i < valsz; i++) {
 224                                 if (i > 0)
 225                                         FPRINTF(fp, ",");
 226                                 if (nvlist_print_json_string(fp, val[i]) == -1)
 227                                         return (-1);
 228                         }
 229                         FPRINTF(fp, "]");
 230                         break;
 231                 }
 232 
 233                 case DATA_TYPE_NVLIST_ARRAY: {
 234                         nvlist_t **val;
 235                         uint_t valsz, i;
 236                         VERIFY0(nvpair_value_nvlist_array(curr, &val, &valsz));
 237                         FPRINTF(fp, "[");
 238                         for (i = 0; i < valsz; i++) {
 239                                 if (i > 0)
 240                                         FPRINTF(fp, ",");
 241                                 if (nvlist_print_json(fp, val[i]) == -1)
 242                                         return (-1);
 243                         }
 244                         FPRINTF(fp, "]");
 245                         break;
 246                 }
 247 
 248                 case DATA_TYPE_BOOLEAN_ARRAY: {
 249                         boolean_t *val;
 250                         uint_t valsz, i;
 251                         VERIFY0(nvpair_value_boolean_array(curr, &val, &valsz));
 252                         FPRINTF(fp, "[");
 253                         for (i = 0; i < valsz; i++) {
 254                                 if (i > 0)
 255                                         FPRINTF(fp, ",");
 256                                 FPRINTF(fp, val[i] == B_TRUE ?
 257                                     "true" : "false");
 258                         }
 259                         FPRINTF(fp, "]");
 260                         break;
 261                 }
 262 
 263                 case DATA_TYPE_BYTE_ARRAY: {
 264                         uchar_t *val;
 265                         uint_t valsz, i;
 266                         VERIFY0(nvpair_value_byte_array(curr, &val, &valsz));
 267                         FPRINTF(fp, "[");
 268                         for (i = 0; i < valsz; i++) {
 269                                 if (i > 0)
 270                                         FPRINTF(fp, ",");
 271                                 FPRINTF(fp, "%hhu", val[i]);
 272                         }
 273                         FPRINTF(fp, "]");
 274                         break;
 275                 }
 276 
 277                 case DATA_TYPE_UINT8_ARRAY: {
 278                         uint8_t *val;
 279                         uint_t valsz, i;
 280                         VERIFY0(nvpair_value_uint8_array(curr, &val, &valsz));
 281                         FPRINTF(fp, "[");
 282                         for (i = 0; i < valsz; i++) {
 283                                 if (i > 0)
 284                                         FPRINTF(fp, ",");
 285                                 FPRINTF(fp, "%hhu", val[i]);
 286                         }
 287                         FPRINTF(fp, "]");
 288                         break;
 289                 }
 290 
 291                 case DATA_TYPE_INT8_ARRAY: {
 292                         int8_t *val;
 293                         uint_t valsz, i;
 294                         VERIFY0(nvpair_value_int8_array(curr, &val, &valsz));
 295                         FPRINTF(fp, "[");
 296                         for (i = 0; i < valsz; i++) {
 297                                 if (i > 0)
 298                                         FPRINTF(fp, ",");
 299                                 FPRINTF(fp, "%hd", val[i]);
 300                         }
 301                         FPRINTF(fp, "]");
 302                         break;
 303                 }
 304 
 305                 case DATA_TYPE_UINT16_ARRAY: {
 306                         uint16_t *val;
 307                         uint_t valsz, i;
 308                         VERIFY0(nvpair_value_uint16_array(curr, &val, &valsz));
 309                         FPRINTF(fp, "[");
 310                         for (i = 0; i < valsz; i++) {
 311                                 if (i > 0)
 312                                         FPRINTF(fp, ",");
 313                                 FPRINTF(fp, "%hu", val[i]);
 314                         }
 315                         FPRINTF(fp, "]");
 316                         break;
 317                 }
 318 
 319                 case DATA_TYPE_INT16_ARRAY: {
 320                         int16_t *val;
 321                         uint_t valsz, i;
 322                         VERIFY0(nvpair_value_int16_array(curr, &val, &valsz));
 323                         FPRINTF(fp, "[");
 324                         for (i = 0; i < valsz; i++) {
 325                                 if (i > 0)
 326                                         FPRINTF(fp, ",");
 327                                 FPRINTF(fp, "%hhd", val[i]);
 328                         }
 329                         FPRINTF(fp, "]");
 330                         break;
 331                 }
 332 
 333                 case DATA_TYPE_UINT32_ARRAY: {
 334                         uint32_t *val;
 335                         uint_t valsz, i;
 336                         VERIFY0(nvpair_value_uint32_array(curr, &val, &valsz));
 337                         FPRINTF(fp, "[");
 338                         for (i = 0; i < valsz; i++) {
 339                                 if (i > 0)
 340                                         FPRINTF(fp, ",");
 341                                 FPRINTF(fp, "%u", val[i]);
 342                         }
 343                         FPRINTF(fp, "]");
 344                         break;
 345                 }
 346 
 347                 case DATA_TYPE_INT32_ARRAY: {
 348                         int32_t *val;
 349                         uint_t valsz, i;
 350                         VERIFY0(nvpair_value_int32_array(curr, &val, &valsz));
 351                         FPRINTF(fp, "[");
 352                         for (i = 0; i < valsz; i++) {
 353                                 if (i > 0)
 354                                         FPRINTF(fp, ",");
 355                                 FPRINTF(fp, "%d", val[i]);
 356                         }
 357                         FPRINTF(fp, "]");
 358                         break;
 359                 }
 360 
 361                 case DATA_TYPE_UINT64_ARRAY: {
 362                         uint64_t *val;
 363                         uint_t valsz, i;
 364                         VERIFY0(nvpair_value_uint64_array(curr, &val, &valsz));
 365                         FPRINTF(fp, "[");
 366                         for (i = 0; i < valsz; i++) {
 367                                 if (i > 0)
 368                                         FPRINTF(fp, ",");
 369                                 FPRINTF(fp, "%llu", val[i]);
 370                         }
 371                         FPRINTF(fp, "]");
 372                         break;
 373                 }
 374 
 375                 case DATA_TYPE_INT64_ARRAY: {
 376                         int64_t *val;
 377                         uint_t valsz, i;
 378                         VERIFY0(nvpair_value_int64_array(curr, &val, &valsz));
 379                         FPRINTF(fp, "[");
 380                         for (i = 0; i < valsz; i++) {
 381                                 if (i > 0)
 382                                         FPRINTF(fp, ",");
 383                                 FPRINTF(fp, "%lld", val[i]);
 384                         }
 385                         FPRINTF(fp, "]");
 386                         break;
 387                 }
 388 
 389                 case DATA_TYPE_UNKNOWN:
 390                         return (-1);
 391                 }
 392         }
 393 
 394         FPRINTF(fp, "}");
 395         return (0);
 396 }