Print this page
OS-1840 fmdump shall emit JSON (newlines, fflush)
OS-1840 fmdump shall emit JSON (small fix 3)
OS-1840 fmdump shall emit JSON (small fix 2)
OS-1840 fmdump shall emit JSON (rm feedback)

Split Close
Expand all
Collapse all
          --- old/usr/src/lib/libnvpair/nvpair_json.c
          +++ new/usr/src/lib/libnvpair/nvpair_json.c
↓ open down ↓ 5 lines elided ↑ open up ↑
   6    6   *
   7    7   * A full copy of the text of the CDDL should have accompanied this
   8    8   * source.  A copy of the CDDL is also available via the Internet at
   9    9   * http://www.illumos.org/license/CDDL.
  10   10   */
  11   11  /*
  12   12   * Copyright (c) 2013, Joyent, Inc. All rights reserved.
  13   13   */
  14   14  
  15   15  #include <stdio.h>
       16 +#include <stdlib.h>
  16   17  #include <strings.h>
       18 +#include <wchar.h>
       19 +#include <sys/debug.h>
  17   20  
  18   21  #include "libnvpair.h"
  19   22  
       23 +#define FPRINTF(fp, ...)                                \
       24 +        do {                                            \
       25 +                if (fprintf(fp, __VA_ARGS__) < 0)       \
       26 +                        return (-1);                    \
       27 +        } while (0)
       28 +
  20   29  /*
  21   30   * When formatting a string for JSON output we must escape certain characters,
  22   31   * as described in RFC4627.  This applies to both member names and
  23   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.
  24   44   */
  25      -static void
       45 +static int
  26   46  nvlist_print_json_string(FILE *fp, const char *input)
  27   47  {
  28      -        char c;
       48 +        mbstate_t mbr;
       49 +        wchar_t c;
       50 +        size_t sz;
  29   51  
  30      -        fprintf(fp, "\"");
  31      -        while ((c = *input++) != '\0') {
       52 +        bzero(&mbr, sizeof (mbr));
       53 +
       54 +        FPRINTF(fp, "\"");
       55 +        while ((sz = mbrtowc(&c, input, MB_CUR_MAX, &mbr)) > 0) {
  32   56                  switch (c) {
  33   57                  case '"':
  34      -                        fprintf(fp, "\\\"");
       58 +                        FPRINTF(fp, "\\\"");
  35   59                          break;
  36   60                  case '\n':
  37      -                        fprintf(fp, "\\n");
       61 +                        FPRINTF(fp, "\\n");
  38   62                          break;
  39   63                  case '\r':
  40      -                        fprintf(fp, "\\r");
       64 +                        FPRINTF(fp, "\\r");
  41   65                          break;
  42   66                  case '\\':
  43      -                        fprintf(fp, "\\\\");
       67 +                        FPRINTF(fp, "\\\\");
  44   68                          break;
  45   69                  case '\f':
  46      -                        fprintf(fp, "\\f");
       70 +                        FPRINTF(fp, "\\f");
  47   71                          break;
  48   72                  case '\t':
  49      -                        fprintf(fp, "\\t");
       73 +                        FPRINTF(fp, "\\t");
  50   74                          break;
  51   75                  case '\b':
  52      -                        fprintf(fp, "\\b");
       76 +                        FPRINTF(fp, "\\b");
  53   77                          break;
  54   78                  default:
  55      -                        if (c >= 0x00 && c <= 0x1f) {
  56      -                                fprintf(fp, "\\u00%02hhu", c);
  57      -                        } else {
  58      -                                fprintf(fp, "%c", c);
       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);
  59   93                          }
  60   94                          break;
  61   95                  }
       96 +                input += sz;
  62   97          }
  63      -        fprintf(fp, "\"");
       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);
  64  109  }
  65  110  
  66      -void
      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
  67  117  nvlist_print_json(FILE *fp, nvlist_t *nvl)
  68  118  {
  69  119          nvpair_t *curr;
  70  120          boolean_t first = B_TRUE;
  71  121  
  72      -        fprintf(fp, "{");
      122 +        FPRINTF(fp, "{");
  73  123  
  74  124          for (curr = nvlist_next_nvpair(nvl, NULL); curr;
  75  125              curr = nvlist_next_nvpair(nvl, curr)) {
  76  126                  data_type_t type = nvpair_type(curr);
  77  127  
  78  128                  if (!first)
  79      -                        fprintf(fp, ",");
      129 +                        FPRINTF(fp, ",");
  80  130                  else
  81  131                          first = B_FALSE;
  82  132  
  83      -                nvlist_print_json_string(fp, nvpair_name(curr));
  84      -                fprintf(fp, ":");
      133 +                if (nvlist_print_json_string(fp, nvpair_name(curr)) == -1)
      134 +                        return (-1);
      135 +                FPRINTF(fp, ":");
  85  136  
  86  137                  switch (type) {
  87  138                  case DATA_TYPE_STRING: {
  88      -                        char *string;
  89      -                        nvpair_value_string(curr, &string);
  90      -                        nvlist_print_json_string(fp, string);
      139 +                        char *string = fnvpair_value_string(curr);
      140 +                        if (nvlist_print_json_string(fp, string) == -1)
      141 +                                return (-1);
  91  142                          break;
  92  143                  }
  93  144  
  94  145                  case DATA_TYPE_BOOLEAN: {
  95      -                        fprintf(fp, "true");
      146 +                        FPRINTF(fp, "true");
  96  147                          break;
  97  148                  }
  98  149  
  99  150                  case DATA_TYPE_BOOLEAN_VALUE: {
 100      -                        boolean_t boolean;
 101      -                        nvpair_value_boolean_value(curr, &boolean);
 102      -                        fprintf(fp, "%s", boolean == B_TRUE ? "true" :
 103      -                            "false");
      151 +                        FPRINTF(fp, "%s", fnvpair_value_boolean_value(curr) ==
      152 +                            B_TRUE ? "true" : "false");
 104  153                          break;
 105  154                  }
 106  155  
 107  156                  case DATA_TYPE_BYTE: {
 108      -                        uchar_t val;
 109      -                        nvpair_value_byte(curr, &val);
 110      -                        fprintf(fp, "%hhu", val);
      157 +                        FPRINTF(fp, "%hhu", fnvpair_value_byte(curr));
 111  158                          break;
 112  159                  }
 113  160  
 114  161                  case DATA_TYPE_INT8: {
 115      -                        int8_t val;
 116      -                        nvpair_value_int8(curr, &val);
 117      -                        fprintf(fp, "%hhd", val);
      162 +                        FPRINTF(fp, "%hhd", fnvpair_value_int8(curr));
 118  163                          break;
 119  164                  }
 120  165  
 121  166                  case DATA_TYPE_UINT8: {
 122      -                        uint8_t val;
 123      -                        nvpair_value_uint8(curr, &val);
 124      -                        fprintf(fp, "%hhu", val);
      167 +                        FPRINTF(fp, "%hhu", fnvpair_value_uint8(curr));
 125  168                          break;
 126  169                  }
 127  170  
 128  171                  case DATA_TYPE_INT16: {
 129      -                        int16_t val;
 130      -                        nvpair_value_int16(curr, &val);
 131      -                        fprintf(fp, "%hd", val);
      172 +                        FPRINTF(fp, "%hd", fnvpair_value_int16(curr));
 132  173                          break;
 133  174                  }
 134  175  
 135  176                  case DATA_TYPE_UINT16: {
 136      -                        uint16_t val;
 137      -                        nvpair_value_uint16(curr, &val);
 138      -                        fprintf(fp, "%hu", val);
      177 +                        FPRINTF(fp, "%hu", fnvpair_value_uint16(curr));
 139  178                          break;
 140  179                  }
 141  180  
 142  181                  case DATA_TYPE_INT32: {
 143      -                        int32_t val;
 144      -                        nvpair_value_int32(curr, &val);
 145      -                        fprintf(fp, "%d", val);
      182 +                        FPRINTF(fp, "%d", fnvpair_value_int32(curr));
 146  183                          break;
 147  184                  }
 148  185  
 149  186                  case DATA_TYPE_UINT32: {
 150      -                        uint32_t val;
 151      -                        nvpair_value_uint32(curr, &val);
 152      -                        fprintf(fp, "%u", val);
      187 +                        FPRINTF(fp, "%u", fnvpair_value_uint32(curr));
 153  188                          break;
 154  189                  }
 155  190  
 156  191                  case DATA_TYPE_INT64: {
 157      -                        int64_t val;
 158      -                        nvpair_value_int64(curr, &val);
 159      -                        fprintf(fp, "%lld", val);
      192 +                        FPRINTF(fp, "%lld", fnvpair_value_int64(curr));
 160  193                          break;
 161  194                  }
 162  195  
 163  196                  case DATA_TYPE_UINT64: {
 164      -                        uint64_t val;
 165      -                        nvpair_value_uint64(curr, &val);
 166      -                        fprintf(fp, "%llu", val);
      197 +                        FPRINTF(fp, "%llu", fnvpair_value_uint64(curr));
 167  198                          break;
 168  199                  }
 169  200  
 170  201                  case DATA_TYPE_HRTIME: {
 171      -                        hrtime_t val;
 172      -                        nvpair_value_hrtime(curr, &val);
 173      -                        fprintf(fp, "%llu", val);
      202 +                        FPRINTF(fp, "%llu", fnvpair_value_hrtime(curr));
 174  203                          break;
 175  204                  }
 176  205  
 177  206                  case DATA_TYPE_DOUBLE: {
 178      -                        double val;
 179      -                        nvpair_value_double(curr, &val);
 180      -                        fprintf(fp, "%f", val);
      207 +                        FPRINTF(fp, "%f", fnvpair_value_double(curr));
 181  208                          break;
 182  209                  }
 183  210  
 184  211                  case DATA_TYPE_NVLIST: {
 185      -                        nvlist_t *val;
 186      -                        nvpair_value_nvlist(curr, &val);
 187      -                        nvlist_print_json(fp, val);
      212 +                        if (nvlist_print_json(fp,
      213 +                            fnvpair_value_nvlist(curr)) == -1)
      214 +                                return (-1);
 188  215                          break;
 189  216                  }
 190  217  
 191  218                  case DATA_TYPE_STRING_ARRAY: {
 192  219                          char **val;
 193      -                        uint_t valsz, pos;
 194      -                        nvpair_value_string_array(curr, &val, &valsz);
 195      -                        fprintf(fp, "[");
 196      -                        for (pos = 0; pos < valsz; pos++) {
 197      -                                if (pos > 0)
 198      -                                        fprintf(fp, ",");
 199      -                                nvlist_print_json_string(fp, val[pos]);
      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);
 200  228                          }
 201      -                        fprintf(fp, "]");
      229 +                        FPRINTF(fp, "]");
 202  230                          break;
 203  231                  }
 204  232  
 205  233                  case DATA_TYPE_NVLIST_ARRAY: {
 206  234                          nvlist_t **val;
 207      -                        uint_t valsz, pos;
 208      -                        nvpair_value_nvlist_array(curr, &val, &valsz);
 209      -                        fprintf(fp, "[");
 210      -                        for (pos = 0; pos < valsz; pos++) {
 211      -                                if (pos > 0)
 212      -                                        fprintf(fp, ",");
 213      -                                nvlist_print_json(fp, val[pos]);
      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);
 214  243                          }
 215      -                        fprintf(fp, "]");
      244 +                        FPRINTF(fp, "]");
 216  245                          break;
 217  246                  }
 218  247  
 219  248                  case DATA_TYPE_BOOLEAN_ARRAY: {
 220  249                          boolean_t *val;
 221      -                        uint_t valsz, pos;
 222      -                        nvpair_value_boolean_array(curr, &val, &valsz);
 223      -                        fprintf(fp, "[");
 224      -                        for (pos = 0; pos < valsz; pos++) {
 225      -                                if (pos > 0)
 226      -                                        fprintf(fp, ",");
 227      -                                fprintf(fp, val[pos] == B_TRUE ?
      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 ?
 228  257                                      "true" : "false");
 229  258                          }
 230      -                        fprintf(fp, "]");
      259 +                        FPRINTF(fp, "]");
 231  260                          break;
 232  261                  }
 233  262  
 234  263                  case DATA_TYPE_BYTE_ARRAY: {
 235  264                          uchar_t *val;
 236      -                        uint_t valsz, pos;
 237      -                        nvpair_value_byte_array(curr, &val, &valsz);
 238      -                        fprintf(fp, "[");
 239      -                        for (pos = 0; pos < valsz; pos++) {
 240      -                                if (pos > 0)
 241      -                                        fprintf(fp, ",");
 242      -                                fprintf(fp, "%hhu", val[pos]);
      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]);
 243  272                          }
 244      -                        fprintf(fp, "]");
      273 +                        FPRINTF(fp, "]");
 245  274                          break;
 246  275                  }
 247  276  
 248  277                  case DATA_TYPE_UINT8_ARRAY: {
 249  278                          uint8_t *val;
 250      -                        uint_t valsz, pos;
 251      -                        nvpair_value_uint8_array(curr, &val, &valsz);
 252      -                        fprintf(fp, "[");
 253      -                        for (pos = 0; pos < valsz; pos++) {
 254      -                                if (pos > 0)
 255      -                                        fprintf(fp, ",");
 256      -                                fprintf(fp, "%hhu", val[pos]);
      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]);
 257  286                          }
 258      -                        fprintf(fp, "]");
      287 +                        FPRINTF(fp, "]");
 259  288                          break;
 260  289                  }
 261  290  
 262  291                  case DATA_TYPE_INT8_ARRAY: {
 263  292                          int8_t *val;
 264      -                        uint_t valsz, pos;
 265      -                        nvpair_value_int8_array(curr, &val, &valsz);
 266      -                        fprintf(fp, "[");
 267      -                        for (pos = 0; pos < valsz; pos++) {
 268      -                                if (pos > 0)
 269      -                                        fprintf(fp, ",");
 270      -                                fprintf(fp, "%hd", val[pos]);
      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]);
 271  300                          }
 272      -                        fprintf(fp, "]");
      301 +                        FPRINTF(fp, "]");
 273  302                          break;
 274  303                  }
 275  304  
 276  305                  case DATA_TYPE_UINT16_ARRAY: {
 277  306                          uint16_t *val;
 278      -                        uint_t valsz, pos;
 279      -                        nvpair_value_uint16_array(curr, &val, &valsz);
 280      -                        fprintf(fp, "[");
 281      -                        for (pos = 0; pos < valsz; pos++) {
 282      -                                if (pos > 0)
 283      -                                        fprintf(fp, ",");
 284      -                                fprintf(fp, "%hu", val[pos]);
      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]);
 285  314                          }
 286      -                        fprintf(fp, "]");
      315 +                        FPRINTF(fp, "]");
 287  316                          break;
 288  317                  }
 289  318  
 290  319                  case DATA_TYPE_INT16_ARRAY: {
 291  320                          int16_t *val;
 292      -                        uint_t valsz, pos;
 293      -                        nvpair_value_int16_array(curr, &val, &valsz);
 294      -                        fprintf(fp, "[");
 295      -                        for (pos = 0; pos < valsz; pos++) {
 296      -                                if (pos > 0)
 297      -                                        fprintf(fp, ",");
 298      -                                fprintf(fp, "%hhd", val[pos]);
      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]);
 299  328                          }
 300      -                        fprintf(fp, "]");
      329 +                        FPRINTF(fp, "]");
 301  330                          break;
 302  331                  }
 303  332  
 304  333                  case DATA_TYPE_UINT32_ARRAY: {
 305  334                          uint32_t *val;
 306      -                        uint_t valsz, pos;
 307      -                        nvpair_value_uint32_array(curr, &val, &valsz);
 308      -                        fprintf(fp, "[");
 309      -                        for (pos = 0; pos < valsz; pos++) {
 310      -                                if (pos > 0)
 311      -                                        fprintf(fp, ",");
 312      -                                fprintf(fp, "%u", val[pos]);
      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]);
 313  342                          }
 314      -                        fprintf(fp, "]");
      343 +                        FPRINTF(fp, "]");
 315  344                          break;
 316  345                  }
 317  346  
 318  347                  case DATA_TYPE_INT32_ARRAY: {
 319  348                          int32_t *val;
 320      -                        uint_t valsz, pos;
 321      -                        nvpair_value_int32_array(curr, &val, &valsz);
 322      -                        fprintf(fp, "[");
 323      -                        for (pos = 0; pos < valsz; pos++) {
 324      -                                if (pos > 0)
 325      -                                        fprintf(fp, ",");
 326      -                                fprintf(fp, "%d", val[pos]);
      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]);
 327  356                          }
 328      -                        fprintf(fp, "]");
      357 +                        FPRINTF(fp, "]");
 329  358                          break;
 330  359                  }
 331  360  
 332  361                  case DATA_TYPE_UINT64_ARRAY: {
 333  362                          uint64_t *val;
 334      -                        uint_t valsz, pos;
 335      -                        nvpair_value_uint64_array(curr, &val, &valsz);
 336      -                        fprintf(fp, "[");
 337      -                        for (pos = 0; pos < valsz; pos++) {
 338      -                                if (pos > 0)
 339      -                                        fprintf(fp, ",");
 340      -                                fprintf(fp, "%llu", val[pos]);
      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]);
 341  370                          }
 342      -                        fprintf(fp, "]");
      371 +                        FPRINTF(fp, "]");
 343  372                          break;
 344  373                  }
 345  374  
 346  375                  case DATA_TYPE_INT64_ARRAY: {
 347  376                          int64_t *val;
 348      -                        uint_t valsz, pos;
 349      -                        nvpair_value_int64_array(curr, &val, &valsz);
 350      -                        fprintf(fp, "[");
 351      -                        for (pos = 0; pos < valsz; pos++) {
 352      -                                if (pos > 0)
 353      -                                        fprintf(fp, ",");
 354      -                                fprintf(fp, "%lld", val[pos]);
      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]);
 355  384                          }
 356      -                        fprintf(fp, "]");
      385 +                        FPRINTF(fp, "]");
 357  386                          break;
 358  387                  }
 359  388  
 360  389                  case DATA_TYPE_UNKNOWN:
 361      -                        fprintf(fp, "null");
 362      -                        break;
      390 +                        return (-1);
 363  391                  }
 364  392          }
 365  393  
 366      -        fprintf(fp, "}");
      394 +        FPRINTF(fp, "}");
      395 +        return (0);
 367  396  }
    
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX