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