diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 18:24:20 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 18:24:20 +0000 |
commit | 483eb2f56657e8e7f419ab1a4fab8dce9ade8609 (patch) | |
tree | e5d88d25d870d5dedacb6bbdbe2a966086a0a5cf /src/spdk/lib/json | |
parent | Initial commit. (diff) | |
download | ceph-483eb2f56657e8e7f419ab1a4fab8dce9ade8609.tar.xz ceph-483eb2f56657e8e7f419ab1a4fab8dce9ade8609.zip |
Adding upstream version 14.2.21.upstream/14.2.21upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | src/spdk/lib/json/Makefile | 40 | ||||
-rw-r--r-- | src/spdk/lib/json/json_parse.c | 668 | ||||
-rw-r--r-- | src/spdk/lib/json/json_util.c | 650 | ||||
-rw-r--r-- | src/spdk/lib/json/json_write.c | 687 | ||||
-rw-r--r-- | src/spdk/lib/jsonrpc/Makefile | 41 | ||||
-rw-r--r-- | src/spdk/lib/jsonrpc/jsonrpc_client.c | 213 | ||||
-rw-r--r-- | src/spdk/lib/jsonrpc/jsonrpc_client_tcp.c | 284 | ||||
-rw-r--r-- | src/spdk/lib/jsonrpc/jsonrpc_internal.h | 149 | ||||
-rw-r--r-- | src/spdk/lib/jsonrpc/jsonrpc_server.c | 360 | ||||
-rw-r--r-- | src/spdk/lib/jsonrpc/jsonrpc_server_tcp.c | 394 |
10 files changed, 3486 insertions, 0 deletions
diff --git a/src/spdk/lib/json/Makefile b/src/spdk/lib/json/Makefile new file mode 100644 index 00000000..8808df9e --- /dev/null +++ b/src/spdk/lib/json/Makefile @@ -0,0 +1,40 @@ +# +# BSD LICENSE +# +# Copyright (c) Intel Corporation. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Intel Corporation nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +SPDK_ROOT_DIR := $(abspath $(CURDIR)/../..) +include $(SPDK_ROOT_DIR)/mk/spdk.common.mk + +C_SRCS = json_parse.c json_util.c json_write.c +LIBNAME = json + +include $(SPDK_ROOT_DIR)/mk/spdk.lib.mk diff --git a/src/spdk/lib/json/json_parse.c b/src/spdk/lib/json/json_parse.c new file mode 100644 index 00000000..8639d5ff --- /dev/null +++ b/src/spdk/lib/json/json_parse.c @@ -0,0 +1,668 @@ +/*- + * BSD LICENSE + * + * Copyright (c) Intel Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "spdk/json.h" + +#include "spdk_internal/utf.h" + +#define SPDK_JSON_MAX_NESTING_DEPTH 64 + +static int +hex_value(uint8_t c) +{ +#define V(x, y) [x] = y + 1 + static const int8_t val[256] = { + V('0', 0), V('1', 1), V('2', 2), V('3', 3), V('4', 4), + V('5', 5), V('6', 6), V('7', 7), V('8', 8), V('9', 9), + V('A', 0xA), V('B', 0xB), V('C', 0xC), V('D', 0xD), V('E', 0xE), V('F', 0xF), + V('a', 0xA), V('b', 0xB), V('c', 0xC), V('d', 0xD), V('e', 0xE), V('f', 0xF), + }; +#undef V + + return val[c] - 1; +} + +static int +json_decode_string_escape_unicode(uint8_t **strp, uint8_t *buf_end, uint8_t *out) +{ + uint8_t *str = *strp; + int v0, v1, v2, v3; + uint32_t val; + uint32_t surrogate_high = 0; + int rc; +decode: + /* \uXXXX */ + assert(buf_end > str); + + if (*str++ != '\\') { return SPDK_JSON_PARSE_INVALID; } + if (buf_end == str) { return SPDK_JSON_PARSE_INCOMPLETE; } + + if (*str++ != 'u') { return SPDK_JSON_PARSE_INVALID; } + if (buf_end == str) { return SPDK_JSON_PARSE_INCOMPLETE; } + + if ((v3 = hex_value(*str++)) < 0) { return SPDK_JSON_PARSE_INVALID; } + if (buf_end == str) { return SPDK_JSON_PARSE_INCOMPLETE; } + + if ((v2 = hex_value(*str++)) < 0) { return SPDK_JSON_PARSE_INVALID; } + if (buf_end == str) { return SPDK_JSON_PARSE_INCOMPLETE; } + + if ((v1 = hex_value(*str++)) < 0) { return SPDK_JSON_PARSE_INVALID; } + if (buf_end == str) { return SPDK_JSON_PARSE_INCOMPLETE; } + + if ((v0 = hex_value(*str++)) < 0) { return SPDK_JSON_PARSE_INVALID; } + if (buf_end == str) { return SPDK_JSON_PARSE_INCOMPLETE; } + + val = v0 | (v1 << 4) | (v2 << 8) | (v3 << 12); + + if (surrogate_high) { + /* We already parsed the high surrogate, so this should be the low part. */ + if (!utf16_valid_surrogate_low(val)) { + return SPDK_JSON_PARSE_INVALID; + } + + /* Convert UTF-16 surrogate pair into codepoint and fall through to utf8_encode. */ + val = utf16_decode_surrogate_pair(surrogate_high, val); + } else if (utf16_valid_surrogate_high(val)) { + surrogate_high = val; + + /* + * We parsed a \uXXXX sequence that decoded to the first half of a + * UTF-16 surrogate pair, so it must be immediately followed by another + * \uXXXX escape. + * + * Loop around to get the low half of the surrogate pair. + */ + if (buf_end == str) { return SPDK_JSON_PARSE_INCOMPLETE; } + goto decode; + } else if (utf16_valid_surrogate_low(val)) { + /* + * We found the second half of surrogate pair without the first half; + * this is an invalid encoding. + */ + return SPDK_JSON_PARSE_INVALID; + } + + /* + * Convert Unicode escape (or surrogate pair) to UTF-8 in place. + * + * This is safe (will not write beyond the buffer) because the \uXXXX sequence is 6 bytes + * (or 12 bytes for surrogate pairs), and the longest possible UTF-8 encoding of a + * single codepoint is 4 bytes. + */ + if (out) { + rc = utf8_encode_unsafe(out, val); + } else { + rc = utf8_codepoint_len(val); + } + if (rc < 0) { + return SPDK_JSON_PARSE_INVALID; + } + + *strp = str; /* update input pointer */ + return rc; /* return number of bytes decoded */ +} + +static int +json_decode_string_escape_twochar(uint8_t **strp, uint8_t *buf_end, uint8_t *out) +{ + static const uint8_t escapes[256] = { + ['b'] = '\b', + ['f'] = '\f', + ['n'] = '\n', + ['r'] = '\r', + ['t'] = '\t', + ['/'] = '/', + ['"'] = '"', + ['\\'] = '\\', + }; + uint8_t *str = *strp; + uint8_t c; + + assert(buf_end > str); + if (buf_end - str < 2) { + return SPDK_JSON_PARSE_INCOMPLETE; + } + + assert(str[0] == '\\'); + + c = escapes[str[1]]; + if (c) { + if (out) { + *out = c; + } + *strp += 2; /* consumed two bytes */ + return 1; /* produced one byte */ + } + + return SPDK_JSON_PARSE_INVALID; +} + +/* + * Decode JSON string backslash escape. + * \param strp pointer to pointer to first character of escape (the backslash). + * *strp is also advanced to indicate how much input was consumed. + * + * \return Number of bytes appended to out + */ +static int +json_decode_string_escape(uint8_t **strp, uint8_t *buf_end, uint8_t *out) +{ + int rc; + + rc = json_decode_string_escape_twochar(strp, buf_end, out); + if (rc > 0) { + return rc; + } + + return json_decode_string_escape_unicode(strp, buf_end, out); +} + +/* + * Decode JSON string in place. + * + * \param str_start Pointer to the beginning of the string (the opening " character). + * + * \return Number of bytes in decoded string (beginning from start). + */ +static int +json_decode_string(uint8_t *str_start, uint8_t *buf_end, uint8_t **str_end, uint32_t flags) +{ + uint8_t *str = str_start; + uint8_t *out = str_start + 1; /* Decode string in place (skip the initial quote) */ + int rc; + + if (buf_end - str_start < 2) { + /* + * Shortest valid string (the empty string) is two bytes (""), + * so this can't possibly be valid + */ + *str_end = str; + return SPDK_JSON_PARSE_INCOMPLETE; + } + + if (*str++ != '"') { + *str_end = str; + return SPDK_JSON_PARSE_INVALID; + } + + while (str < buf_end) { + if (str[0] == '"') { + /* + * End of string. + * Update str_end to point at next input byte and return output length. + */ + *str_end = str + 1; + return out - str_start - 1; + } else if (str[0] == '\\') { + rc = json_decode_string_escape(&str, buf_end, + flags & SPDK_JSON_PARSE_FLAG_DECODE_IN_PLACE ? out : NULL); + assert(rc != 0); + if (rc < 0) { + *str_end = str; + return rc; + } + out += rc; + } else if (str[0] <= 0x1f) { + /* control characters must be escaped */ + *str_end = str; + return SPDK_JSON_PARSE_INVALID; + } else { + rc = utf8_valid(str, buf_end); + if (rc == 0) { + *str_end = str; + return SPDK_JSON_PARSE_INCOMPLETE; + } else if (rc < 0) { + *str_end = str; + return SPDK_JSON_PARSE_INVALID; + } + + if (out && out != str && (flags & SPDK_JSON_PARSE_FLAG_DECODE_IN_PLACE)) { + memmove(out, str, rc); + } + out += rc; + str += rc; + } + } + + /* If execution gets here, we ran out of buffer. */ + *str_end = str; + return SPDK_JSON_PARSE_INCOMPLETE; +} + +static int +json_valid_number(uint8_t *start, uint8_t *buf_end) +{ + uint8_t *p = start; + uint8_t c; + + if (p >= buf_end) { return -1; } + + c = *p++; + if (c >= '1' && c <= '9') { goto num_int_digits; } + if (c == '0') { goto num_frac_or_exp; } + if (c == '-') { goto num_int_first_digit; } + p--; + goto done_invalid; + +num_int_first_digit: + if (spdk_likely(p != buf_end)) { + c = *p++; + if (c == '0') { goto num_frac_or_exp; } + if (c >= '1' && c <= '9') { goto num_int_digits; } + p--; + } + goto done_invalid; + +num_int_digits: + if (spdk_likely(p != buf_end)) { + c = *p++; + if (c >= '0' && c <= '9') { goto num_int_digits; } + if (c == '.') { goto num_frac_first_digit; } + if (c == 'e' || c == 'E') { goto num_exp_sign; } + p--; + } + goto done_valid; + +num_frac_or_exp: + if (spdk_likely(p != buf_end)) { + c = *p++; + if (c == '.') { goto num_frac_first_digit; } + if (c == 'e' || c == 'E') { goto num_exp_sign; } + p--; + } + goto done_valid; + +num_frac_first_digit: + if (spdk_likely(p != buf_end)) { + c = *p++; + if (c >= '0' && c <= '9') { goto num_frac_digits; } + p--; + } + goto done_invalid; + +num_frac_digits: + if (spdk_likely(p != buf_end)) { + c = *p++; + if (c >= '0' && c <= '9') { goto num_frac_digits; } + if (c == 'e' || c == 'E') { goto num_exp_sign; } + p--; + } + goto done_valid; + +num_exp_sign: + if (spdk_likely(p != buf_end)) { + c = *p++; + if (c >= '0' && c <= '9') { goto num_exp_digits; } + if (c == '-' || c == '+') { goto num_exp_first_digit; } + p--; + } + goto done_invalid; + +num_exp_first_digit: + if (spdk_likely(p != buf_end)) { + c = *p++; + if (c >= '0' && c <= '9') { goto num_exp_digits; } + p--; + } + goto done_invalid; + +num_exp_digits: + if (spdk_likely(p != buf_end)) { + c = *p++; + if (c >= '0' && c <= '9') { goto num_exp_digits; } + p--; + } + goto done_valid; + +done_valid: + /* Valid end state */ + return p - start; + +done_invalid: + /* Invalid end state */ + if (p == buf_end) { + /* Hit the end of the buffer - the stream is incomplete. */ + return SPDK_JSON_PARSE_INCOMPLETE; + } + + /* Found an invalid character in an invalid end state */ + return SPDK_JSON_PARSE_INVALID; +} + +static int +json_valid_comment(const uint8_t *start, const uint8_t *buf_end) +{ + const uint8_t *p = start; + bool multiline; + + assert(buf_end > p); + if (buf_end - p < 2) { + return SPDK_JSON_PARSE_INCOMPLETE; + } + + if (p[0] != '/') { + return SPDK_JSON_PARSE_INVALID; + } + if (p[1] == '*') { + multiline = true; + } else if (p[1] == '/') { + multiline = false; + } else { + return SPDK_JSON_PARSE_INVALID; + } + p += 2; + + if (multiline) { + while (p != buf_end - 1) { + if (p[0] == '*' && p[1] == '/') { + /* Include the terminating star and slash in the comment */ + return p - start + 2; + } + p++; + } + } else { + while (p != buf_end) { + if (*p == '\r' || *p == '\n') { + /* Do not include the line terminator in the comment */ + return p - start; + } + p++; + } + } + + return SPDK_JSON_PARSE_INCOMPLETE; +} + +struct json_literal { + enum spdk_json_val_type type; + uint32_t len; + uint8_t str[8]; +}; + +/* + * JSON only defines 3 possible literals; they can be uniquely identified by bits + * 3 and 4 of the first character: + * 'f' = 0b11[00]110 + * 'n' = 0b11[01]110 + * 't' = 0b11[10]100 + * These two bits can be used as an index into the g_json_literals array. + */ +static const struct json_literal g_json_literals[] = { + {SPDK_JSON_VAL_FALSE, 5, "false"}, + {SPDK_JSON_VAL_NULL, 4, "null"}, + {SPDK_JSON_VAL_TRUE, 4, "true"}, + {} +}; + +static int +match_literal(const uint8_t *start, const uint8_t *end, const uint8_t *literal, size_t len) +{ + assert(end >= start); + if ((size_t)(end - start) < len) { + return SPDK_JSON_PARSE_INCOMPLETE; + } + + if (memcmp(start, literal, len) != 0) { + return SPDK_JSON_PARSE_INVALID; + } + + return len; +} + +ssize_t +spdk_json_parse(void *json, size_t size, struct spdk_json_val *values, size_t num_values, + void **end, uint32_t flags) +{ + uint8_t *json_end = json + size; + enum spdk_json_val_type containers[SPDK_JSON_MAX_NESTING_DEPTH]; + size_t con_value[SPDK_JSON_MAX_NESTING_DEPTH]; + enum spdk_json_val_type con_type = SPDK_JSON_VAL_INVALID; + bool trailing_comma = false; + size_t depth = 0; /* index into containers */ + size_t cur_value = 0; /* index into values */ + size_t con_start_value; + uint8_t *data = json; + uint8_t *new_data; + int rc = 0; + const struct json_literal *lit; + enum { + STATE_VALUE, /* initial state */ + STATE_VALUE_SEPARATOR, /* value separator (comma) */ + STATE_NAME, /* "name": value */ + STATE_NAME_SEPARATOR, /* colon */ + STATE_END, /* parsed the complete value, so only whitespace is valid */ + } state = STATE_VALUE; + +#define ADD_VALUE(t, val_start_ptr, val_end_ptr) \ + if (values && cur_value < num_values) { \ + values[cur_value].type = t; \ + values[cur_value].start = val_start_ptr; \ + values[cur_value].len = val_end_ptr - val_start_ptr; \ + } \ + cur_value++ + + while (data < json_end) { + uint8_t c = *data; + + switch (c) { + case ' ': + case '\t': + case '\r': + case '\n': + /* Whitespace is allowed between any tokens. */ + data++; + break; + + case 't': + case 'f': + case 'n': + /* true, false, or null */ + if (state != STATE_VALUE) { goto done_invalid; } + lit = &g_json_literals[(c >> 3) & 3]; /* See comment above g_json_literals[] */ + assert(lit->str[0] == c); + rc = match_literal(data, json_end, lit->str, lit->len); + if (rc < 0) { goto done_rc; } + ADD_VALUE(lit->type, data, data + rc); + data += rc; + state = depth ? STATE_VALUE_SEPARATOR : STATE_END; + trailing_comma = false; + break; + + case '"': + if (state != STATE_VALUE && state != STATE_NAME) { goto done_invalid; } + rc = json_decode_string(data, json_end, &new_data, flags); + if (rc < 0) { + data = new_data; + goto done_rc; + } + /* + * Start is data + 1 to skip initial quote. + * Length is data + rc - 1 to skip both quotes. + */ + ADD_VALUE(state == STATE_VALUE ? SPDK_JSON_VAL_STRING : SPDK_JSON_VAL_NAME, + data + 1, data + rc - 1); + data = new_data; + if (state == STATE_NAME) { + state = STATE_NAME_SEPARATOR; + } else { + state = depth ? STATE_VALUE_SEPARATOR : STATE_END; + } + trailing_comma = false; + break; + + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (state != STATE_VALUE) { goto done_invalid; } + rc = json_valid_number(data, json_end); + if (rc < 0) { goto done_rc; } + ADD_VALUE(SPDK_JSON_VAL_NUMBER, data, data + rc); + data += rc; + state = depth ? STATE_VALUE_SEPARATOR : STATE_END; + trailing_comma = false; + break; + + case '{': + case '[': + if (state != STATE_VALUE) { goto done_invalid; } + if (depth == SPDK_JSON_MAX_NESTING_DEPTH) { + rc = SPDK_JSON_PARSE_MAX_DEPTH_EXCEEDED; + goto done_rc; + } + if (c == '{') { + con_type = SPDK_JSON_VAL_OBJECT_BEGIN; + state = STATE_NAME; + } else { + con_type = SPDK_JSON_VAL_ARRAY_BEGIN; + state = STATE_VALUE; + } + con_value[depth] = cur_value; + containers[depth++] = con_type; + ADD_VALUE(con_type, data, data + 1); + data++; + trailing_comma = false; + break; + + case '}': + case ']': + if (trailing_comma) { goto done_invalid; } + if (depth == 0) { goto done_invalid; } + con_type = containers[--depth]; + con_start_value = con_value[depth]; + if (values && con_start_value < num_values) { + values[con_start_value].len = cur_value - con_start_value - 1; + } + if (c == '}') { + if (state != STATE_NAME && state != STATE_VALUE_SEPARATOR) { + goto done_invalid; + } + if (con_type != SPDK_JSON_VAL_OBJECT_BEGIN) { + goto done_invalid; + } + ADD_VALUE(SPDK_JSON_VAL_OBJECT_END, data, data + 1); + } else { + if (state != STATE_VALUE && state != STATE_VALUE_SEPARATOR) { + goto done_invalid; + } + if (con_type != SPDK_JSON_VAL_ARRAY_BEGIN) { + goto done_invalid; + } + ADD_VALUE(SPDK_JSON_VAL_ARRAY_END, data, data + 1); + } + con_type = depth == 0 ? SPDK_JSON_VAL_INVALID : containers[depth - 1]; + data++; + state = depth ? STATE_VALUE_SEPARATOR : STATE_END; + trailing_comma = false; + break; + + case ',': + if (state != STATE_VALUE_SEPARATOR) { goto done_invalid; } + data++; + assert(con_type == SPDK_JSON_VAL_ARRAY_BEGIN || + con_type == SPDK_JSON_VAL_OBJECT_BEGIN); + state = con_type == SPDK_JSON_VAL_ARRAY_BEGIN ? STATE_VALUE : STATE_NAME; + trailing_comma = true; + break; + + case ':': + if (state != STATE_NAME_SEPARATOR) { goto done_invalid; } + data++; + state = STATE_VALUE; + break; + + case '/': + if (!(flags & SPDK_JSON_PARSE_FLAG_ALLOW_COMMENTS)) { + goto done_invalid; + } + rc = json_valid_comment(data, json_end); + if (rc < 0) { goto done_rc; } + /* Skip over comment */ + data += rc; + break; + + default: + goto done_invalid; + } + + if (state == STATE_END) { + break; + } + } + + if (state == STATE_END) { + /* Skip trailing whitespace */ + while (data < json_end) { + uint8_t c = *data; + + if (c == ' ' || c == '\t' || c == '\r' || c == '\n') { + data++; + } else { + break; + } + } + + /* + * These asserts are just for sanity checking - they are guaranteed by the allowed + * state transitions. + */ + assert(depth == 0); + assert(trailing_comma == false); + assert(data <= json_end); + if (end) { + *end = data; + } + return cur_value; + } + + /* Invalid end state - ran out of data */ + rc = SPDK_JSON_PARSE_INCOMPLETE; + +done_rc: + assert(rc < 0); + if (end) { + *end = data; + } + return rc; + +done_invalid: + rc = SPDK_JSON_PARSE_INVALID; + goto done_rc; +} diff --git a/src/spdk/lib/json/json_util.c b/src/spdk/lib/json/json_util.c new file mode 100644 index 00000000..1146e6fa --- /dev/null +++ b/src/spdk/lib/json/json_util.c @@ -0,0 +1,650 @@ +/*- + * BSD LICENSE + * + * Copyright (c) Intel Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "spdk/json.h" + +#include "spdk_internal/utf.h" +#include "spdk_internal/log.h" + +#define SPDK_JSON_DEBUG(...) SPDK_DEBUGLOG(SPDK_LOG_JSON_UTIL, __VA_ARGS__) + +size_t +spdk_json_val_len(const struct spdk_json_val *val) +{ + if (val == NULL) { + return 0; + } + + if (val->type == SPDK_JSON_VAL_ARRAY_BEGIN || val->type == SPDK_JSON_VAL_OBJECT_BEGIN) { + return val->len + 2; + } + + return 1; +} + +bool +spdk_json_strequal(const struct spdk_json_val *val, const char *str) +{ + size_t len; + + if (val->type != SPDK_JSON_VAL_STRING && val->type != SPDK_JSON_VAL_NAME) { + return false; + } + + len = strlen(str); + if (val->len != len) { + return false; + } + + return memcmp(val->start, str, len) == 0; +} + +char * +spdk_json_strdup(const struct spdk_json_val *val) +{ + size_t len; + char *s; + + if (val->type != SPDK_JSON_VAL_STRING && val->type != SPDK_JSON_VAL_NAME) { + return NULL; + } + + len = val->len; + + if (memchr(val->start, '\0', len)) { + /* String contains embedded NUL, so it is not a valid C string. */ + return NULL; + } + + s = malloc(len + 1); + if (s == NULL) { + return s; + } + + memcpy(s, val->start, len); + s[len] = '\0'; + + return s; +} + +struct spdk_json_num { + bool negative; + uint64_t significand; + int64_t exponent; +}; + +static int +spdk_json_number_split(const struct spdk_json_val *val, struct spdk_json_num *num) +{ + const char *iter; + size_t remaining; + uint64_t *pval; + uint64_t frac_digits = 0; + uint64_t exponent_u64 = 0; + bool exponent_negative = false; + enum { + NUM_STATE_INT, + NUM_STATE_FRAC, + NUM_STATE_EXP, + } state; + + memset(num, 0, sizeof(*num)); + + if (val->type != SPDK_JSON_VAL_NUMBER) { + return -EINVAL; + } + + remaining = val->len; + if (remaining == 0) { + return -EINVAL; + } + + iter = val->start; + if (*iter == '-') { + num->negative = true; + iter++; + remaining--; + } + + state = NUM_STATE_INT; + pval = &num->significand; + while (remaining--) { + char c = *iter++; + + if (c == '.') { + state = NUM_STATE_FRAC; + } else if (c == 'e' || c == 'E') { + state = NUM_STATE_EXP; + pval = &exponent_u64; + } else if (c == '-') { + assert(state == NUM_STATE_EXP); + exponent_negative = true; + } else if (c == '+') { + assert(state == NUM_STATE_EXP); + /* exp_negative = false; */ /* already false by default */ + } else { + uint64_t new_val; + + assert(c >= '0' && c <= '9'); + new_val = *pval * 10 + c - '0'; + if (new_val < *pval) { + return -ERANGE; + } + + if (state == NUM_STATE_FRAC) { + frac_digits++; + } + + *pval = new_val; + } + } + + if (exponent_negative) { + if (exponent_u64 > 9223372036854775808ULL) { /* abs(INT64_MIN) */ + return -ERANGE; + } + num->exponent = (int64_t) - exponent_u64; + } else { + if (exponent_u64 > INT64_MAX) { + return -ERANGE; + } + num->exponent = exponent_u64; + } + num->exponent -= frac_digits; + + /* Apply as much of the exponent as possible without overflow or truncation */ + if (num->exponent < 0) { + while (num->exponent && num->significand >= 10 && num->significand % 10 == 0) { + num->significand /= 10; + num->exponent++; + } + } else { /* positive exponent */ + while (num->exponent) { + uint64_t new_val = num->significand * 10; + + if (new_val < num->significand) { + break; + } + + num->significand = new_val; + num->exponent--; + } + } + + return 0; +} + +int +spdk_json_number_to_uint16(const struct spdk_json_val *val, uint16_t *num) +{ + struct spdk_json_num split_num; + int rc; + + rc = spdk_json_number_split(val, &split_num); + if (rc) { + return rc; + } + + if (split_num.exponent || split_num.negative) { + return -ERANGE; + } + + if (split_num.significand > UINT16_MAX) { + return -ERANGE; + } + *num = (uint16_t)split_num.significand; + return 0; +} + +int +spdk_json_number_to_int32(const struct spdk_json_val *val, int32_t *num) +{ + struct spdk_json_num split_num; + int rc; + + rc = spdk_json_number_split(val, &split_num); + if (rc) { + return rc; + } + + if (split_num.exponent) { + return -ERANGE; + } + + if (split_num.negative) { + if (split_num.significand > 2147483648) { /* abs(INT32_MIN) */ + return -ERANGE; + } + *num = (int32_t) - (int64_t)split_num.significand; + return 0; + } + + /* positive */ + if (split_num.significand > INT32_MAX) { + return -ERANGE; + } + *num = (int32_t)split_num.significand; + return 0; +} + +int +spdk_json_number_to_uint32(const struct spdk_json_val *val, uint32_t *num) +{ + struct spdk_json_num split_num; + int rc; + + rc = spdk_json_number_split(val, &split_num); + if (rc) { + return rc; + } + + if (split_num.exponent || split_num.negative) { + return -ERANGE; + } + + if (split_num.significand > UINT32_MAX) { + return -ERANGE; + } + *num = (uint32_t)split_num.significand; + return 0; +} + +int +spdk_json_number_to_uint64(const struct spdk_json_val *val, uint64_t *num) +{ + struct spdk_json_num split_num; + int rc; + + rc = spdk_json_number_split(val, &split_num); + if (rc) { + return rc; + } + + if (split_num.exponent || split_num.negative) { + return -ERANGE; + } + + *num = split_num.significand; + return 0; +} + +int +spdk_json_decode_object(const struct spdk_json_val *values, + const struct spdk_json_object_decoder *decoders, size_t num_decoders, void *out) +{ + uint32_t i; + bool invalid = false; + size_t decidx; + bool *seen; + + if (values == NULL || values->type != SPDK_JSON_VAL_OBJECT_BEGIN) { + return -1; + } + + seen = calloc(sizeof(bool), num_decoders); + if (seen == NULL) { + return -1; + } + + for (i = 0; i < values->len;) { + const struct spdk_json_val *name = &values[i + 1]; + const struct spdk_json_val *v = &values[i + 2]; + bool found = false; + + for (decidx = 0; decidx < num_decoders; decidx++) { + const struct spdk_json_object_decoder *dec = &decoders[decidx]; + if (spdk_json_strequal(name, dec->name)) { + void *field = (void *)((uintptr_t)out + dec->offset); + + found = true; + + if (seen[decidx]) { + /* duplicate field name */ + invalid = true; + } else { + seen[decidx] = true; + if (dec->decode_func(v, field)) { + invalid = true; + /* keep going to fill out any other valid keys */ + } + } + break; + } + } + + if (!found) { + invalid = true; + } + + i += 1 + spdk_json_val_len(v); + } + + for (decidx = 0; decidx < num_decoders; decidx++) { + if (!decoders[decidx].optional && !seen[decidx]) { + /* required field is missing */ + invalid = true; + break; + } + } + + free(seen); + return invalid ? -1 : 0; +} + +int +spdk_json_decode_array(const struct spdk_json_val *values, spdk_json_decode_fn decode_func, + void *out, size_t max_size, size_t *out_size, size_t stride) +{ + uint32_t i; + char *field; + char *out_end; + + if (values == NULL || values->type != SPDK_JSON_VAL_ARRAY_BEGIN) { + return -1; + } + + *out_size = 0; + field = out; + out_end = field + max_size * stride; + for (i = 0; i < values->len;) { + const struct spdk_json_val *v = &values[i + 1]; + + if (field == out_end) { + return -1; + } + + if (decode_func(v, field)) { + return -1; + } + + i += spdk_json_val_len(v); + field += stride; + (*out_size)++; + } + + return 0; +} + +int +spdk_json_decode_bool(const struct spdk_json_val *val, void *out) +{ + bool *f = out; + + if (val->type != SPDK_JSON_VAL_TRUE && val->type != SPDK_JSON_VAL_FALSE) { + return -1; + } + + *f = val->type == SPDK_JSON_VAL_TRUE; + return 0; +} + +int +spdk_json_decode_uint16(const struct spdk_json_val *val, void *out) +{ + uint16_t *i = out; + + return spdk_json_number_to_uint16(val, i); +} + +int +spdk_json_decode_int32(const struct spdk_json_val *val, void *out) +{ + int32_t *i = out; + + return spdk_json_number_to_int32(val, i); +} + +int +spdk_json_decode_uint32(const struct spdk_json_val *val, void *out) +{ + uint32_t *i = out; + + return spdk_json_number_to_uint32(val, i); +} + +int +spdk_json_decode_uint64(const struct spdk_json_val *val, void *out) +{ + uint64_t *i = out; + + return spdk_json_number_to_uint64(val, i); +} + +int +spdk_json_decode_string(const struct spdk_json_val *val, void *out) +{ + char **s = out; + + free(*s); + + *s = spdk_json_strdup(val); + + if (*s) { + return 0; + } else { + return -1; + } +} + +static struct spdk_json_val * +spdk_json_first(struct spdk_json_val *object, enum spdk_json_val_type type) +{ + /* 'object' must be JSON object or array. 'type' might be combination of these two. */ + assert((type & (SPDK_JSON_VAL_ARRAY_BEGIN | SPDK_JSON_VAL_OBJECT_BEGIN)) != 0); + + assert(object != NULL); + + if ((object->type & type) == 0) { + return NULL; + } + + object++; + if (object->len == 0) { + return NULL; + } + + return object; +} + +static struct spdk_json_val * +spdk_json_value(struct spdk_json_val *key) +{ + return key->type == SPDK_JSON_VAL_NAME ? key + 1 : NULL; +} + +int +spdk_json_find(struct spdk_json_val *object, const char *key_name, struct spdk_json_val **key, + struct spdk_json_val **val, enum spdk_json_val_type type) +{ + struct spdk_json_val *_key = NULL; + struct spdk_json_val *_val = NULL; + struct spdk_json_val *it; + + assert(object != NULL); + + for (it = spdk_json_first(object, SPDK_JSON_VAL_ARRAY_BEGIN | SPDK_JSON_VAL_OBJECT_BEGIN); + it != NULL; + it = spdk_json_next(it)) { + if (it->type != SPDK_JSON_VAL_NAME) { + continue; + } + + if (spdk_json_strequal(it, key_name) != true) { + continue; + } + + if (_key) { + SPDK_JSON_DEBUG("Duplicate key '%s'", key_name); + return -EINVAL; + } + + _key = it; + _val = spdk_json_value(_key); + + if (type != SPDK_JSON_VAL_INVALID && (_val->type & type) == 0) { + SPDK_JSON_DEBUG("key '%s' type is %#x but expected one of %#x\n", key_name, _val->type, type); + return -EDOM; + } + } + + if (key) { + *key = _key; + } + + if (val) { + *val = _val; + } + + return _val ? 0 : -ENOENT; +} + +int +spdk_json_find_string(struct spdk_json_val *object, const char *key_name, + struct spdk_json_val **key, struct spdk_json_val **val) +{ + return spdk_json_find(object, key_name, key, val, SPDK_JSON_VAL_STRING); +} + +int +spdk_json_find_array(struct spdk_json_val *object, const char *key_name, + struct spdk_json_val **key, struct spdk_json_val **val) +{ + return spdk_json_find(object, key_name, key, val, SPDK_JSON_VAL_ARRAY_BEGIN); +} + +struct spdk_json_val * +spdk_json_object_first(struct spdk_json_val *object) +{ + struct spdk_json_val *first = spdk_json_first(object, SPDK_JSON_VAL_OBJECT_BEGIN); + + /* Empty object? */ + return first && first->type != SPDK_JSON_VAL_OBJECT_END ? first : NULL; +} + +struct spdk_json_val * +spdk_json_array_first(struct spdk_json_val *array_begin) +{ + struct spdk_json_val *first = spdk_json_first(array_begin, SPDK_JSON_VAL_ARRAY_BEGIN); + + /* Empty array? */ + return first && first->type != SPDK_JSON_VAL_ARRAY_END ? first : NULL; +} + +static struct spdk_json_val * +spdk_json_skip_object_or_array(struct spdk_json_val *val) +{ + unsigned lvl; + enum spdk_json_val_type end_type; + struct spdk_json_val *it; + + if (val->type == SPDK_JSON_VAL_OBJECT_BEGIN) { + end_type = SPDK_JSON_VAL_OBJECT_END; + } else if (val->type == SPDK_JSON_VAL_ARRAY_BEGIN) { + end_type = SPDK_JSON_VAL_ARRAY_END; + } else { + SPDK_JSON_DEBUG("Expected JSON object (%#x) or array (%#x) but got %#x\n", + SPDK_JSON_VAL_OBJECT_BEGIN, SPDK_JSON_VAL_ARRAY_END, val->type); + return NULL; + } + + lvl = 1; + for (it = val + 1; it->type != SPDK_JSON_VAL_INVALID && lvl != 0; it++) { + if (it->type == val->type) { + lvl++; + } else if (it->type == end_type) { + lvl--; + } + } + + /* if lvl != 0 we have invalid JSON object */ + if (lvl != 0) { + SPDK_JSON_DEBUG("Can't find end of object (type: %#x): lvl (%u) != 0)\n", val->type, lvl); + it = NULL; + } + + return it; +} + +struct spdk_json_val * +spdk_json_next(struct spdk_json_val *it) +{ + struct spdk_json_val *val, *next; + + switch (it->type) { + case SPDK_JSON_VAL_NAME: + val = spdk_json_value(it); + next = spdk_json_next(val); + break; + + /* We are in the middle of an array - get to next entry */ + case SPDK_JSON_VAL_NULL: + case SPDK_JSON_VAL_TRUE: + case SPDK_JSON_VAL_FALSE: + case SPDK_JSON_VAL_NUMBER: + case SPDK_JSON_VAL_STRING: + val = it + 1; + return val; + + case SPDK_JSON_VAL_ARRAY_BEGIN: + case SPDK_JSON_VAL_OBJECT_BEGIN: + next = spdk_json_skip_object_or_array(it); + break; + + /* Can't go to the next object if started from the end of array or object */ + case SPDK_JSON_VAL_ARRAY_END: + case SPDK_JSON_VAL_OBJECT_END: + case SPDK_JSON_VAL_INVALID: + return NULL; + default: + assert(false); + return NULL; + + } + + /* EOF ? */ + if (next == NULL) { + return NULL; + } + + switch (next->type) { + case SPDK_JSON_VAL_ARRAY_END: + case SPDK_JSON_VAL_OBJECT_END: + case SPDK_JSON_VAL_INVALID: + return NULL; + default: + /* Next value */ + return next; + } +} + +SPDK_LOG_REGISTER_COMPONENT("json_util", SPDK_LOG_JSON_UTIL) diff --git a/src/spdk/lib/json/json_write.c b/src/spdk/lib/json/json_write.c new file mode 100644 index 00000000..0cd600be --- /dev/null +++ b/src/spdk/lib/json/json_write.c @@ -0,0 +1,687 @@ +/*- + * BSD LICENSE + * + * Copyright (c) Intel Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "spdk/json.h" + +#include "spdk_internal/utf.h" + +struct spdk_json_write_ctx { + spdk_json_write_cb write_cb; + void *cb_ctx; + uint32_t flags; + uint32_t indent; + bool new_indent; + bool first_value; + bool failed; + size_t buf_filled; + uint8_t buf[4096]; +}; + +static int emit_buf_full(struct spdk_json_write_ctx *w, const void *data, size_t size); + +static int +fail(struct spdk_json_write_ctx *w) +{ + w->failed = true; + return -1; +} + +static int +flush_buf(struct spdk_json_write_ctx *w) +{ + int rc; + + rc = w->write_cb(w->cb_ctx, w->buf, w->buf_filled); + if (rc != 0) { + return fail(w); + } + + w->buf_filled = 0; + + return 0; +} + +struct spdk_json_write_ctx * +spdk_json_write_begin(spdk_json_write_cb write_cb, void *cb_ctx, uint32_t flags) +{ + struct spdk_json_write_ctx *w; + + w = calloc(1, sizeof(*w)); + if (w == NULL) { + return w; + } + + w->write_cb = write_cb; + w->cb_ctx = cb_ctx; + w->flags = flags; + w->indent = 0; + w->new_indent = false; + w->first_value = true; + w->failed = false; + w->buf_filled = 0; + + return w; +} + +int +spdk_json_write_end(struct spdk_json_write_ctx *w) +{ + bool failed; + int rc; + + if (w == NULL) { + return 0; + } + + failed = w->failed; + + rc = flush_buf(w); + if (rc != 0) { + failed = true; + } + + free(w); + + return failed ? -1 : 0; +} + +static inline int +emit(struct spdk_json_write_ctx *w, const void *data, size_t size) +{ + size_t buf_remain = sizeof(w->buf) - w->buf_filled; + + if (spdk_unlikely(size > buf_remain)) { + /* Not enough space in buffer for the new data. */ + return emit_buf_full(w, data, size); + } + + /* Copy the new data into buf. */ + memcpy(w->buf + w->buf_filled, data, size); + w->buf_filled += size; + return 0; +} + +static int +emit_buf_full(struct spdk_json_write_ctx *w, const void *data, size_t size) +{ + size_t buf_remain = sizeof(w->buf) - w->buf_filled; + int rc; + + assert(size > buf_remain); + + /* Copy as much of the new data as possible into the buffer and flush it. */ + memcpy(w->buf + w->buf_filled, data, buf_remain); + w->buf_filled += buf_remain; + + rc = flush_buf(w); + if (rc != 0) { + return fail(w); + } + + /* Recurse to emit the rest of the data. */ + return emit(w, data + buf_remain, size - buf_remain); +} + +static int +emit_fmt(struct spdk_json_write_ctx *w, const void *data, size_t size) +{ + if (w->flags & SPDK_JSON_WRITE_FLAG_FORMATTED) { + return emit(w, data, size); + } + return 0; +} + +static int +emit_indent(struct spdk_json_write_ctx *w) +{ + uint32_t i; + + if (w->flags & SPDK_JSON_WRITE_FLAG_FORMATTED) { + for (i = 0; i < w->indent; i++) { + if (emit(w, " ", 2)) { return fail(w); } + } + } + return 0; +} + +static int +begin_value(struct spdk_json_write_ctx *w) +{ + // TODO: check for value state + if (w->new_indent) { + if (emit_fmt(w, "\n", 1)) { return fail(w); } + if (emit_indent(w)) { return fail(w); } + } + if (!w->first_value) { + if (emit(w, ",", 1)) { return fail(w); } + if (emit_fmt(w, "\n", 1)) { return fail(w); } + if (emit_indent(w)) { return fail(w); } + } + w->first_value = false; + w->new_indent = false; + return 0; +} + +int +spdk_json_write_val_raw(struct spdk_json_write_ctx *w, const void *data, size_t len) +{ + if (begin_value(w)) { return fail(w); } + return emit(w, data, len); +} + +int +spdk_json_write_null(struct spdk_json_write_ctx *w) +{ + if (begin_value(w)) { return fail(w); } + return emit(w, "null", 4); +} + +int +spdk_json_write_bool(struct spdk_json_write_ctx *w, bool val) +{ + if (begin_value(w)) { return fail(w); } + if (val) { + return emit(w, "true", 4); + } else { + return emit(w, "false", 5); + } +} + +int +spdk_json_write_int32(struct spdk_json_write_ctx *w, int32_t val) +{ + char buf[32]; + int count; + + if (begin_value(w)) { return fail(w); } + count = snprintf(buf, sizeof(buf), "%" PRId32, val); + if (count <= 0 || (size_t)count >= sizeof(buf)) { return fail(w); } + return emit(w, buf, count); +} + +int +spdk_json_write_uint32(struct spdk_json_write_ctx *w, uint32_t val) +{ + char buf[32]; + int count; + + if (begin_value(w)) { return fail(w); } + count = snprintf(buf, sizeof(buf), "%" PRIu32, val); + if (count <= 0 || (size_t)count >= sizeof(buf)) { return fail(w); } + return emit(w, buf, count); +} + +int +spdk_json_write_int64(struct spdk_json_write_ctx *w, int64_t val) +{ + char buf[32]; + int count; + + if (begin_value(w)) { return fail(w); } + count = snprintf(buf, sizeof(buf), "%" PRId64, val); + if (count <= 0 || (size_t)count >= sizeof(buf)) { return fail(w); } + return emit(w, buf, count); +} + +int +spdk_json_write_uint64(struct spdk_json_write_ctx *w, uint64_t val) +{ + char buf[32]; + int count; + + if (begin_value(w)) { return fail(w); } + count = snprintf(buf, sizeof(buf), "%" PRIu64, val); + if (count <= 0 || (size_t)count >= sizeof(buf)) { return fail(w); } + return emit(w, buf, count); +} + +static void +write_hex_4(void *dest, uint16_t val) +{ + uint8_t *p = dest; + char hex[] = "0123456789ABCDEF"; + + p[0] = hex[(val >> 12)]; + p[1] = hex[(val >> 8) & 0xF]; + p[2] = hex[(val >> 4) & 0xF]; + p[3] = hex[val & 0xF]; +} + +static inline int +write_codepoint(struct spdk_json_write_ctx *w, uint32_t codepoint) +{ + static const uint8_t escapes[] = { + ['\b'] = 'b', + ['\f'] = 'f', + ['\n'] = 'n', + ['\r'] = 'r', + ['\t'] = 't', + ['"'] = '"', + ['\\'] = '\\', + /* + * Forward slash (/) is intentionally not converted to an escape + * (it is valid unescaped). + */ + }; + uint16_t high, low; + char out[13]; + size_t out_len; + + if (codepoint < sizeof(escapes) && escapes[codepoint]) { + out[0] = '\\'; + out[1] = escapes[codepoint]; + out_len = 2; + } else if (codepoint >= 0x20 && codepoint < 0x7F) { + /* + * Encode plain ASCII directly (except 0x7F, since it is really + * a control character, despite the JSON spec not considering it one). + */ + out[0] = (uint8_t)codepoint; + out_len = 1; + } else if (codepoint < 0x10000) { + out[0] = '\\'; + out[1] = 'u'; + write_hex_4(&out[2], (uint16_t)codepoint); + out_len = 6; + } else { + utf16_encode_surrogate_pair(codepoint, &high, &low); + out[0] = '\\'; + out[1] = 'u'; + write_hex_4(&out[2], high); + out[6] = '\\'; + out[7] = 'u'; + write_hex_4(&out[8], low); + out_len = 12; + } + + return emit(w, out, out_len); +} + +static int +write_string_or_name(struct spdk_json_write_ctx *w, const char *val, size_t len) +{ + const uint8_t *p = val; + const uint8_t *end = val + len; + + if (emit(w, "\"", 1)) { return fail(w); } + + while (p != end) { + int codepoint_len; + uint32_t codepoint; + + codepoint_len = utf8_valid(p, end); + switch (codepoint_len) { + case 1: + codepoint = utf8_decode_unsafe_1(p); + break; + case 2: + codepoint = utf8_decode_unsafe_2(p); + break; + case 3: + codepoint = utf8_decode_unsafe_3(p); + break; + case 4: + codepoint = utf8_decode_unsafe_4(p); + break; + default: + return fail(w); + } + + if (write_codepoint(w, codepoint)) { return fail(w); } + p += codepoint_len; + } + + return emit(w, "\"", 1); +} + +static int +write_string_or_name_utf16le(struct spdk_json_write_ctx *w, const uint16_t *val, size_t len) +{ + const uint16_t *p = val; + const uint16_t *end = val + len; + + if (emit(w, "\"", 1)) { return fail(w); } + + while (p != end) { + int codepoint_len; + uint32_t codepoint; + + codepoint_len = utf16le_valid(p, end); + switch (codepoint_len) { + case 1: + codepoint = from_le16(&p[0]); + break; + case 2: + codepoint = utf16_decode_surrogate_pair(from_le16(&p[0]), from_le16(&p[1])); + break; + default: + return fail(w); + } + + if (write_codepoint(w, codepoint)) { return fail(w); } + p += codepoint_len; + } + + return emit(w, "\"", 1); +} + +int +spdk_json_write_string_raw(struct spdk_json_write_ctx *w, const char *val, size_t len) +{ + if (begin_value(w)) { return fail(w); } + return write_string_or_name(w, val, len); +} + +int +spdk_json_write_string(struct spdk_json_write_ctx *w, const char *val) +{ + return spdk_json_write_string_raw(w, val, strlen(val)); +} + +int +spdk_json_write_string_utf16le_raw(struct spdk_json_write_ctx *w, const uint16_t *val, size_t len) +{ + if (begin_value(w)) { return fail(w); } + return write_string_or_name_utf16le(w, val, len); +} + +int +spdk_json_write_string_utf16le(struct spdk_json_write_ctx *w, const uint16_t *val) +{ + const uint16_t *p; + size_t len; + + for (len = 0, p = val; *p; p++) { + len++; + } + + return spdk_json_write_string_utf16le_raw(w, val, len); +} + +int +spdk_json_write_string_fmt(struct spdk_json_write_ctx *w, const char *fmt, ...) +{ + va_list args; + int rc; + + va_start(args, fmt); + rc = spdk_json_write_string_fmt_v(w, fmt, args); + va_end(args); + + return rc; +} + +int +spdk_json_write_string_fmt_v(struct spdk_json_write_ctx *w, const char *fmt, va_list args) +{ + char *s; + int rc; + + s = spdk_vsprintf_alloc(fmt, args); + if (s == NULL) { + return -1; + } + + rc = spdk_json_write_string(w, s); + free(s); + return rc; +} + +int +spdk_json_write_array_begin(struct spdk_json_write_ctx *w) +{ + if (begin_value(w)) { return fail(w); } + w->first_value = true; + w->new_indent = true; + w->indent++; + if (emit(w, "[", 1)) { return fail(w); } + return 0; +} + +int +spdk_json_write_array_end(struct spdk_json_write_ctx *w) +{ + w->first_value = false; + if (w->indent == 0) { return fail(w); } + w->indent--; + if (!w->new_indent) { + if (emit_fmt(w, "\n", 1)) { return fail(w); } + if (emit_indent(w)) { return fail(w); } + } + w->new_indent = false; + return emit(w, "]", 1); +} + +int +spdk_json_write_object_begin(struct spdk_json_write_ctx *w) +{ + if (begin_value(w)) { return fail(w); } + w->first_value = true; + w->new_indent = true; + w->indent++; + if (emit(w, "{", 1)) { return fail(w); } + return 0; +} + +int +spdk_json_write_object_end(struct spdk_json_write_ctx *w) +{ + w->first_value = false; + w->indent--; + if (!w->new_indent) { + if (emit_fmt(w, "\n", 1)) { return fail(w); } + if (emit_indent(w)) { return fail(w); } + } + w->new_indent = false; + return emit(w, "}", 1); +} + +int +spdk_json_write_name_raw(struct spdk_json_write_ctx *w, const char *name, size_t len) +{ + /* TODO: check that container is an object */ + if (begin_value(w)) { return fail(w); } + if (write_string_or_name(w, name, len)) { return fail(w); } + w->first_value = true; + if (emit(w, ":", 1)) { return fail(w); } + return emit_fmt(w, " ", 1); +} + +int +spdk_json_write_name(struct spdk_json_write_ctx *w, const char *name) +{ + return spdk_json_write_name_raw(w, name, strlen(name)); +} + +int +spdk_json_write_val(struct spdk_json_write_ctx *w, const struct spdk_json_val *val) +{ + size_t num_values, i; + + switch (val->type) { + case SPDK_JSON_VAL_NUMBER: + return spdk_json_write_val_raw(w, val->start, val->len); + + case SPDK_JSON_VAL_STRING: + return spdk_json_write_string_raw(w, val->start, val->len); + + case SPDK_JSON_VAL_NAME: + return spdk_json_write_name_raw(w, val->start, val->len); + + case SPDK_JSON_VAL_TRUE: + return spdk_json_write_bool(w, true); + + case SPDK_JSON_VAL_FALSE: + return spdk_json_write_bool(w, false); + + case SPDK_JSON_VAL_NULL: + return spdk_json_write_null(w); + + case SPDK_JSON_VAL_ARRAY_BEGIN: + case SPDK_JSON_VAL_OBJECT_BEGIN: + num_values = val[0].len; + + if (val[0].type == SPDK_JSON_VAL_OBJECT_BEGIN) { + if (spdk_json_write_object_begin(w)) { + return fail(w); + } + } else { + if (spdk_json_write_array_begin(w)) { + return fail(w); + } + } + + // Loop up to and including the _END value + for (i = 0; i < num_values + 1;) { + if (spdk_json_write_val(w, &val[i + 1])) { + return fail(w); + } + if (val[i + 1].type == SPDK_JSON_VAL_ARRAY_BEGIN || + val[i + 1].type == SPDK_JSON_VAL_OBJECT_BEGIN) { + i += val[i + 1].len + 2; + } else { + i++; + } + } + return 0; + + case SPDK_JSON_VAL_ARRAY_END: + return spdk_json_write_array_end(w); + + case SPDK_JSON_VAL_OBJECT_END: + return spdk_json_write_object_end(w); + + case SPDK_JSON_VAL_INVALID: + // Handle INVALID to make the compiler happy (and catch other unhandled types) + return fail(w); + } + + return fail(w); +} + +int spdk_json_write_named_null(struct spdk_json_write_ctx *w, const char *name) +{ + int rc = spdk_json_write_name(w, name); + return rc ? rc : spdk_json_write_null(w); +} + +int spdk_json_write_named_bool(struct spdk_json_write_ctx *w, const char *name, bool val) +{ + int rc = spdk_json_write_name(w, name); + + return rc ? rc : spdk_json_write_bool(w, val); +} + +int spdk_json_write_named_int32(struct spdk_json_write_ctx *w, const char *name, int32_t val) +{ + int rc = spdk_json_write_name(w, name); + + return rc ? rc : spdk_json_write_int32(w, val); +} + +int spdk_json_write_named_uint32(struct spdk_json_write_ctx *w, const char *name, uint32_t val) +{ + int rc = spdk_json_write_name(w, name); + + return rc ? rc : spdk_json_write_uint32(w, val); +} + +int spdk_json_write_named_uint64(struct spdk_json_write_ctx *w, const char *name, uint64_t val) +{ + int rc = spdk_json_write_name(w, name); + + return rc ? rc : spdk_json_write_uint64(w, val); +} + +int spdk_json_write_named_int64(struct spdk_json_write_ctx *w, const char *name, int64_t val) +{ + int rc = spdk_json_write_name(w, name); + + return rc ? rc : spdk_json_write_int64(w, val); +} + +int spdk_json_write_named_string(struct spdk_json_write_ctx *w, const char *name, const char *val) +{ + int rc = spdk_json_write_name(w, name); + + return rc ? rc : spdk_json_write_string(w, val); +} + +int spdk_json_write_named_string_fmt(struct spdk_json_write_ctx *w, const char *name, + const char *fmt, ...) +{ + va_list args; + int rc; + + va_start(args, fmt); + rc = spdk_json_write_named_string_fmt_v(w, name, fmt, args); + va_end(args); + + return rc; +} + +int spdk_json_write_named_string_fmt_v(struct spdk_json_write_ctx *w, const char *name, + const char *fmt, va_list args) +{ + char *s; + int rc; + + rc = spdk_json_write_name(w, name); + if (rc) { + return rc; + } + + s = spdk_vsprintf_alloc(fmt, args); + + if (s == NULL) { + return -1; + } + + rc = spdk_json_write_string(w, s); + free(s); + return rc; +} + +int spdk_json_write_named_array_begin(struct spdk_json_write_ctx *w, const char *name) +{ + int rc = spdk_json_write_name(w, name); + + return rc ? rc : spdk_json_write_array_begin(w); +} + +int spdk_json_write_named_object_begin(struct spdk_json_write_ctx *w, const char *name) +{ + int rc = spdk_json_write_name(w, name); + + return rc ? rc : spdk_json_write_object_begin(w); +} diff --git a/src/spdk/lib/jsonrpc/Makefile b/src/spdk/lib/jsonrpc/Makefile new file mode 100644 index 00000000..dd323f1e --- /dev/null +++ b/src/spdk/lib/jsonrpc/Makefile @@ -0,0 +1,41 @@ +# +# BSD LICENSE +# +# Copyright (c) Intel Corporation. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Intel Corporation nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +SPDK_ROOT_DIR := $(abspath $(CURDIR)/../..) +include $(SPDK_ROOT_DIR)/mk/spdk.common.mk + +LIBNAME = jsonrpc +C_SRCS = jsonrpc_server.c jsonrpc_server_tcp.c +C_SRCS += jsonrpc_client.c jsonrpc_client_tcp.c + +include $(SPDK_ROOT_DIR)/mk/spdk.lib.mk diff --git a/src/spdk/lib/jsonrpc/jsonrpc_client.c b/src/spdk/lib/jsonrpc/jsonrpc_client.c new file mode 100644 index 00000000..2426f4e3 --- /dev/null +++ b/src/spdk/lib/jsonrpc/jsonrpc_client.c @@ -0,0 +1,213 @@ +/*- + * BSD LICENSE + * + * Copyright (c) Intel Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "spdk/util.h" +#include "jsonrpc_internal.h" + +struct jsonrpc_response { + const struct spdk_json_val *version; + const struct spdk_json_val *id; + const struct spdk_json_val *result; +}; + +static int +capture_string(const struct spdk_json_val *val, void *out) +{ + const struct spdk_json_val **vptr = out; + + if (spdk_json_strequal(val, "2.0") != true) { + return SPDK_JSON_PARSE_INVALID; + } + + *vptr = val; + return 0; +} + +static int +capture_id(const struct spdk_json_val *val, void *out) +{ + const struct spdk_json_val **vptr = out; + + if (val->type != SPDK_JSON_VAL_STRING && val->type != SPDK_JSON_VAL_NUMBER) { + return SPDK_JSON_PARSE_INVALID; + } + + *vptr = val; + return 0; +} + +static int +capture_any(const struct spdk_json_val *val, void *out) +{ + const struct spdk_json_val **vptr = out; + + *vptr = val; + return 0; +} + +static const struct spdk_json_object_decoder jsonrpc_response_decoders[] = { + {"jsonrpc", offsetof(struct jsonrpc_response, version), capture_string}, + {"id", offsetof(struct jsonrpc_response, id), capture_id}, + {"result", offsetof(struct jsonrpc_response, result), capture_any}, +}; + +static int +parse_single_response(struct spdk_json_val *values, + spdk_jsonrpc_client_response_parser parser_fn, + void *parser_ctx) +{ + struct jsonrpc_response resp = {}; + + if (spdk_json_decode_object(values, jsonrpc_response_decoders, + SPDK_COUNTOF(jsonrpc_response_decoders), + &resp)) { + return SPDK_JSON_PARSE_INVALID; + } + + return parser_fn(parser_ctx, resp.result); +} + +int +spdk_jsonrpc_parse_response(struct spdk_jsonrpc_client *client, void *json, size_t size) +{ + ssize_t rc; + void *end = NULL; + + /* Check to see if we have received a full JSON value. */ + rc = spdk_json_parse(json, size, NULL, 0, &end, 0); + if (rc == SPDK_JSON_PARSE_INCOMPLETE) { + return rc; + } + + SPDK_DEBUGLOG(SPDK_LOG_RPC_CLIENT, "Json string is :\n%s\n", (char *)json); + if (rc < 0 || rc > SPDK_JSONRPC_MAX_VALUES) { + SPDK_ERRLOG("JSON parse error\n"); + /* + * Can't recover from parse error (no guaranteed resync point in streaming JSON). + * Return an error to indicate that the connection should be closed. + */ + return SPDK_JSON_PARSE_INVALID; + } + + /* Decode a second time now that there is a full JSON value available. */ + rc = spdk_json_parse(json, size, client->values, SPDK_JSONRPC_MAX_VALUES, &end, + SPDK_JSON_PARSE_FLAG_DECODE_IN_PLACE); + if (rc < 0 || rc > SPDK_JSONRPC_MAX_VALUES) { + SPDK_ERRLOG("JSON parse error on second pass\n"); + return SPDK_JSON_PARSE_INVALID; + } + + assert(end != NULL); + + if (client->values[0].type != SPDK_JSON_VAL_OBJECT_BEGIN) { + SPDK_ERRLOG("top-level JSON value was not object\n"); + return SPDK_JSON_PARSE_INVALID; + } + + rc = parse_single_response(client->values, client->parser_fn, client->parser_ctx); + + return rc; +} + +static int +jsonrpc_client_write_cb(void *cb_ctx, const void *data, size_t size) +{ + struct spdk_jsonrpc_client_request *request = cb_ctx; + size_t new_size = request->send_buf_size; + + while (new_size - request->send_len < size) { + if (new_size >= SPDK_JSONRPC_SEND_BUF_SIZE_MAX) { + SPDK_ERRLOG("Send buf exceeded maximum size (%zu)\n", + (size_t)SPDK_JSONRPC_SEND_BUF_SIZE_MAX); + return -ENOSPC; + } + + new_size *= 2; + } + + if (new_size != request->send_buf_size) { + uint8_t *new_buf; + + new_buf = realloc(request->send_buf, new_size); + if (new_buf == NULL) { + SPDK_ERRLOG("Resizing send_buf failed (current size %zu, new size %zu)\n", + request->send_buf_size, new_size); + return -ENOMEM; + } + + request->send_buf = new_buf; + request->send_buf_size = new_size; + } + + memcpy(request->send_buf + request->send_len, data, size); + request->send_len += size; + + return 0; +} + +struct spdk_json_write_ctx * +spdk_jsonrpc_begin_request(struct spdk_jsonrpc_client_request *request, int32_t id, + const char *method) +{ + struct spdk_json_write_ctx *w; + + w = spdk_json_write_begin(jsonrpc_client_write_cb, request, 0); + if (w == NULL) { + return NULL; + } + + spdk_json_write_object_begin(w); + spdk_json_write_named_string(w, "jsonrpc", "2.0"); + + if (id >= 0) { + spdk_json_write_named_int32(w, "id", id); + } + + if (method) { + spdk_json_write_named_string(w, "method", method); + } + + return w; +} + +void +spdk_jsonrpc_end_request(struct spdk_jsonrpc_client_request *request, struct spdk_json_write_ctx *w) +{ + assert(w != NULL); + + spdk_json_write_object_end(w); + spdk_json_write_end(w); + jsonrpc_client_write_cb(request, "\n", 1); +} + +SPDK_LOG_REGISTER_COMPONENT("rpc_client", SPDK_LOG_RPC_CLIENT) diff --git a/src/spdk/lib/jsonrpc/jsonrpc_client_tcp.c b/src/spdk/lib/jsonrpc/jsonrpc_client_tcp.c new file mode 100644 index 00000000..a7696c84 --- /dev/null +++ b/src/spdk/lib/jsonrpc/jsonrpc_client_tcp.c @@ -0,0 +1,284 @@ +/*- + * BSD LICENSE + * + * Copyright (c) Intel Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "spdk/string.h" +#include "jsonrpc_internal.h" + +#define RPC_DEFAULT_PORT "5260" + +static struct spdk_jsonrpc_client * +_spdk_jsonrpc_client_connect(int domain, int protocol, + struct sockaddr *server_addr, socklen_t addrlen) +{ + struct spdk_jsonrpc_client *client; + int rc; + + client = calloc(1, sizeof(struct spdk_jsonrpc_client)); + if (client == NULL) { + return NULL; + } + + client->sockfd = socket(domain, SOCK_STREAM, protocol); + if (client->sockfd < 0) { + SPDK_ERRLOG("socket() failed\n"); + free(client); + return NULL; + } + + rc = connect(client->sockfd, server_addr, addrlen); + if (rc != 0) { + SPDK_ERRLOG("could not connet JSON-RPC server: %s\n", spdk_strerror(errno)); + close(client->sockfd); + free(client); + return NULL; + } + + /* memory malloc for recv-buf */ + client->recv_buf = malloc(SPDK_JSONRPC_SEND_BUF_SIZE_INIT); + if (!client->recv_buf) { + SPDK_ERRLOG("memory malloc for recv-buf failed\n"); + close(client->sockfd); + free(client); + return NULL; + } + client->recv_buf_size = SPDK_JSONRPC_SEND_BUF_SIZE_INIT; + + return client; +} + +struct spdk_jsonrpc_client * +spdk_jsonrpc_client_connect(const char *rpc_sock_addr, int addr_family) +{ + struct spdk_jsonrpc_client *client; + + if (addr_family == AF_UNIX) { + /* Unix Domain Socket */ + struct sockaddr_un rpc_sock_addr_unix = {}; + int rc; + + rpc_sock_addr_unix.sun_family = AF_UNIX; + rc = snprintf(rpc_sock_addr_unix.sun_path, + sizeof(rpc_sock_addr_unix.sun_path), + "%s", rpc_sock_addr); + if (rc < 0 || (size_t)rc >= sizeof(rpc_sock_addr_unix.sun_path)) { + SPDK_ERRLOG("RPC Listen address Unix socket path too long\n"); + return NULL; + } + + client = _spdk_jsonrpc_client_connect(AF_UNIX, 0, + (struct sockaddr *)&rpc_sock_addr_unix, + sizeof(rpc_sock_addr_unix)); + } else { + /* TCP/IP socket */ + struct addrinfo hints; + struct addrinfo *res; + char *tmp; + char *host, *port; + + tmp = strdup(rpc_sock_addr); + if (!tmp) { + SPDK_ERRLOG("Out of memory\n"); + return NULL; + } + + if (spdk_parse_ip_addr(tmp, &host, &port) < 0) { + free(tmp); + SPDK_ERRLOG("Invalid listen address '%s'\n", rpc_sock_addr); + return NULL; + } + + if (port == NULL) { + port = RPC_DEFAULT_PORT; + } + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + if (getaddrinfo(host, port, &hints, &res) != 0) { + free(tmp); + SPDK_ERRLOG("Unable to look up RPC connnect address '%s'\n", rpc_sock_addr); + return NULL; + } + + client = _spdk_jsonrpc_client_connect(res->ai_family, res->ai_protocol, + res->ai_addr, res->ai_addrlen); + + freeaddrinfo(res); + free(tmp); + } + + return client; +} + +void +spdk_jsonrpc_client_close(struct spdk_jsonrpc_client *client) +{ + if (client->sockfd >= 0) { + close(client->sockfd); + free(client->recv_buf); + client->sockfd = -1; + } + + free(client); +} + +struct spdk_jsonrpc_client_request * +spdk_jsonrpc_client_create_request(void) +{ + struct spdk_jsonrpc_client_request *request; + + request = calloc(1, sizeof(*request)); + if (request == NULL) { + return NULL; + } + + /* memory malloc for send-buf */ + request->send_buf = malloc(SPDK_JSONRPC_SEND_BUF_SIZE_INIT); + if (!request->send_buf) { + SPDK_ERRLOG("memory malloc for send-buf failed\n"); + free(request); + return NULL; + } + request->send_buf_size = SPDK_JSONRPC_SEND_BUF_SIZE_INIT; + + return request; +} + +void +spdk_jsonrpc_client_free_request(struct spdk_jsonrpc_client_request *req) +{ + free(req->send_buf); + free(req); +} + +int +spdk_jsonrpc_client_send_request(struct spdk_jsonrpc_client *client, + struct spdk_jsonrpc_client_request *request) +{ + ssize_t rc; + + /* Reset offset in request */ + request->send_offset = 0; + + while (request->send_len > 0) { + rc = send(client->sockfd, request->send_buf + request->send_offset, + request->send_len, 0); + if (rc <= 0) { + if (rc < 0 && errno == EINTR) { + rc = 0; + } else { + return rc; + } + } + + request->send_offset += rc; + request->send_len -= rc; + } + + return 0; +} + +static int +recv_buf_expand(struct spdk_jsonrpc_client *client) +{ + uint8_t *new_buf; + + if (client->recv_buf_size * 2 > SPDK_JSONRPC_SEND_BUF_SIZE_MAX) { + return -ENOSPC; + } + + new_buf = realloc(client->recv_buf, client->recv_buf_size * 2); + if (new_buf == NULL) { + SPDK_ERRLOG("Resizing recv_buf failed (current size %zu, new size %zu)\n", + client->recv_buf_size, client->recv_buf_size * 2); + return -ENOMEM; + } + + client->recv_buf = new_buf; + client->recv_buf_size *= 2; + + return 0; +} + +int +spdk_jsonrpc_client_recv_response(struct spdk_jsonrpc_client *client, + spdk_jsonrpc_client_response_parser parser_fn, + void *parser_ctx) +{ + ssize_t rc = 0; + size_t recv_avail; + size_t recv_offset = 0; + + client->parser_fn = parser_fn; + client->parser_ctx = parser_ctx; + + recv_avail = client->recv_buf_size; + + while (recv_avail > 0) { + rc = recv(client->sockfd, client->recv_buf + recv_offset, recv_avail, 0); + if (rc < 0) { + if (errno == EINTR) { + continue; + } else { + return errno; + } + } else if (rc == 0) { + return -EIO; + } + + recv_offset += rc; + recv_avail -= rc; + + /* Check to see if we have received a full JSON value. */ + rc = spdk_jsonrpc_parse_response(client, client->recv_buf, recv_offset); + if (rc == 0) { + /* Successfully parsed response */ + return 0; + } else if (rc != SPDK_JSON_PARSE_INCOMPLETE) { + SPDK_ERRLOG("jsonrpc parse request failed\n"); + return -EINVAL; + } + + /* Expand receive buffer if larger one is needed */ + if (recv_avail == 0) { + rc = recv_buf_expand(client); + if (rc != 0) { + return rc; + } + recv_avail = client->recv_buf_size - recv_offset; + } + } + + return 0; +} diff --git a/src/spdk/lib/jsonrpc/jsonrpc_internal.h b/src/spdk/lib/jsonrpc/jsonrpc_internal.h new file mode 100644 index 00000000..87355fdb --- /dev/null +++ b/src/spdk/lib/jsonrpc/jsonrpc_internal.h @@ -0,0 +1,149 @@ +/*- + * BSD LICENSE + * + * Copyright (c) Intel Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SPDK_JSONRPC_INTERNAL_H_ +#define SPDK_JSONRPC_INTERNAL_H_ + +#include "spdk/stdinc.h" + +#include "spdk/jsonrpc.h" + +#include "spdk_internal/log.h" + +#define SPDK_JSONRPC_RECV_BUF_SIZE (32 * 1024) +#define SPDK_JSONRPC_SEND_BUF_SIZE_INIT (32 * 1024) +#define SPDK_JSONRPC_SEND_BUF_SIZE_MAX (32 * 1024 * 1024) +#define SPDK_JSONRPC_ID_MAX_LEN 128 +#define SPDK_JSONRPC_MAX_CONNS 64 +#define SPDK_JSONRPC_MAX_VALUES 1024 + +struct spdk_jsonrpc_request { + struct spdk_jsonrpc_server_conn *conn; + + /* Copy of request id value */ + struct spdk_json_val id; + uint8_t id_data[SPDK_JSONRPC_ID_MAX_LEN]; + + /* Total space allocated for send_buf */ + size_t send_buf_size; + + /* Number of bytes used in send_buf (<= send_buf_size) */ + size_t send_len; + + size_t send_offset; + + uint8_t *send_buf; + + STAILQ_ENTRY(spdk_jsonrpc_request) link; +}; + +struct spdk_jsonrpc_server_conn { + struct spdk_jsonrpc_server *server; + int sockfd; + bool closed; + struct spdk_json_val values[SPDK_JSONRPC_MAX_VALUES]; + size_t recv_len; + uint8_t recv_buf[SPDK_JSONRPC_RECV_BUF_SIZE]; + uint32_t outstanding_requests; + + pthread_spinlock_t queue_lock; + STAILQ_HEAD(, spdk_jsonrpc_request) send_queue; + + struct spdk_jsonrpc_request *send_request; + + TAILQ_ENTRY(spdk_jsonrpc_server_conn) link; +}; + +struct spdk_jsonrpc_server { + int sockfd; + spdk_jsonrpc_handle_request_fn handle_request; + + TAILQ_HEAD(, spdk_jsonrpc_server_conn) free_conns; + TAILQ_HEAD(, spdk_jsonrpc_server_conn) conns; + + struct spdk_jsonrpc_server_conn conns_array[SPDK_JSONRPC_MAX_CONNS]; +}; + +struct spdk_jsonrpc_client_request { + /* Total space allocated for send_buf */ + size_t send_buf_size; + + /* Number of bytes used in send_buf (<= send_buf_size) */ + size_t send_len; + + size_t send_offset; + + uint8_t *send_buf; +}; + +struct spdk_jsonrpc_client { + int sockfd; + + struct spdk_json_val values[SPDK_JSONRPC_MAX_VALUES]; + size_t recv_buf_size; + uint8_t *recv_buf; + + spdk_jsonrpc_client_response_parser parser_fn; + void *parser_ctx; +}; + +/* jsonrpc_server_tcp */ +void spdk_jsonrpc_server_handle_request(struct spdk_jsonrpc_request *request, + const struct spdk_json_val *method, + const struct spdk_json_val *params); +void spdk_jsonrpc_server_handle_error(struct spdk_jsonrpc_request *request, int error); + +/* Might be called from any thread */ +void spdk_jsonrpc_server_send_response(struct spdk_jsonrpc_request *request); + +/* jsonrpc_server */ +int spdk_jsonrpc_parse_request(struct spdk_jsonrpc_server_conn *conn, void *json, size_t size); + +/* Must be called only from server poll thread */ +void spdk_jsonrpc_free_request(struct spdk_jsonrpc_request *request); + +/* + * Parse JSON data as RPC command response. + * + * \param client structure pointer of jsonrpc client + * \param json Raw JSON data; must be encoded in UTF-8. + * \param size Size of data in bytes. + * + * \return 0 On success + * SPDK_JSON_PARSE_INCOMPLETE If the provided data is not a complete JSON value + * SPDK_JSON_PARSE_INVALID if the provided data has invalid JSON syntax. + */ +int spdk_jsonrpc_parse_response(struct spdk_jsonrpc_client *client, void *json, + size_t size); + +#endif diff --git a/src/spdk/lib/jsonrpc/jsonrpc_server.c b/src/spdk/lib/jsonrpc/jsonrpc_server.c new file mode 100644 index 00000000..6e2a5b2c --- /dev/null +++ b/src/spdk/lib/jsonrpc/jsonrpc_server.c @@ -0,0 +1,360 @@ +/*- + * BSD LICENSE + * + * Copyright (c) Intel Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "jsonrpc_internal.h" + +#include "spdk/util.h" + +struct jsonrpc_request { + const struct spdk_json_val *version; + const struct spdk_json_val *method; + const struct spdk_json_val *params; + const struct spdk_json_val *id; +}; + +static int +capture_val(const struct spdk_json_val *val, void *out) +{ + const struct spdk_json_val **vptr = out; + + *vptr = val; + return 0; +} + +static const struct spdk_json_object_decoder jsonrpc_request_decoders[] = { + {"jsonrpc", offsetof(struct jsonrpc_request, version), capture_val, true}, + {"method", offsetof(struct jsonrpc_request, method), capture_val}, + {"params", offsetof(struct jsonrpc_request, params), capture_val, true}, + {"id", offsetof(struct jsonrpc_request, id), capture_val, true}, +}; + +static void +parse_single_request(struct spdk_jsonrpc_request *request, struct spdk_json_val *values) +{ + bool invalid = false; + struct jsonrpc_request req = {}; + + if (spdk_json_decode_object(values, jsonrpc_request_decoders, + SPDK_COUNTOF(jsonrpc_request_decoders), + &req)) { + invalid = true; + goto done; + } + + if (req.version && (req.version->type != SPDK_JSON_VAL_STRING || + !spdk_json_strequal(req.version, "2.0"))) { + invalid = true; + } + + if (!req.method || req.method->type != SPDK_JSON_VAL_STRING) { + req.method = NULL; + invalid = true; + } + + if (req.id) { + if (req.id->type == SPDK_JSON_VAL_STRING || + req.id->type == SPDK_JSON_VAL_NUMBER) { + /* Copy value into request */ + if (req.id->len <= SPDK_JSONRPC_ID_MAX_LEN) { + request->id.type = req.id->type; + request->id.len = req.id->len; + memcpy(request->id.start, req.id->start, req.id->len); + } else { + SPDK_DEBUGLOG(SPDK_LOG_RPC, "JSON-RPC request id too long (%u)\n", + req.id->len); + invalid = true; + } + } else if (req.id->type == SPDK_JSON_VAL_NULL) { + request->id.type = SPDK_JSON_VAL_NULL; + } else { + invalid = true; + } + } + + if (req.params) { + if (req.params->type != SPDK_JSON_VAL_ARRAY_BEGIN && + req.params->type != SPDK_JSON_VAL_OBJECT_BEGIN) { + req.params = NULL; + invalid = true; + } + } + +done: + if (invalid) { + spdk_jsonrpc_server_handle_error(request, SPDK_JSONRPC_ERROR_INVALID_REQUEST); + } else { + spdk_jsonrpc_server_handle_request(request, req.method, req.params); + } +} + +int +spdk_jsonrpc_parse_request(struct spdk_jsonrpc_server_conn *conn, void *json, size_t size) +{ + struct spdk_jsonrpc_request *request; + ssize_t rc; + void *end = NULL; + + /* Check to see if we have received a full JSON value. */ + rc = spdk_json_parse(json, size, NULL, 0, &end, 0); + if (rc == SPDK_JSON_PARSE_INCOMPLETE) { + return 0; + } + + request = calloc(1, sizeof(*request)); + if (request == NULL) { + SPDK_DEBUGLOG(SPDK_LOG_RPC, "Out of memory allocating request\n"); + return -1; + } + + conn->outstanding_requests++; + + request->conn = conn; + request->id.start = request->id_data; + request->id.len = 0; + request->id.type = SPDK_JSON_VAL_INVALID; + request->send_offset = 0; + request->send_len = 0; + request->send_buf_size = SPDK_JSONRPC_SEND_BUF_SIZE_INIT; + request->send_buf = malloc(request->send_buf_size); + if (request->send_buf == NULL) { + SPDK_ERRLOG("Failed to allocate send_buf (%zu bytes)\n", request->send_buf_size); + conn->outstanding_requests--; + free(request); + return -1; + } + + if (rc < 0 || rc > SPDK_JSONRPC_MAX_VALUES) { + SPDK_DEBUGLOG(SPDK_LOG_RPC, "JSON parse error\n"); + spdk_jsonrpc_server_handle_error(request, SPDK_JSONRPC_ERROR_PARSE_ERROR); + + /* + * Can't recover from parse error (no guaranteed resync point in streaming JSON). + * Return an error to indicate that the connection should be closed. + */ + return -1; + } + + /* Decode a second time now that there is a full JSON value available. */ + rc = spdk_json_parse(json, size, conn->values, SPDK_JSONRPC_MAX_VALUES, &end, + SPDK_JSON_PARSE_FLAG_DECODE_IN_PLACE); + if (rc < 0 || rc > SPDK_JSONRPC_MAX_VALUES) { + SPDK_DEBUGLOG(SPDK_LOG_RPC, "JSON parse error on second pass\n"); + spdk_jsonrpc_server_handle_error(request, SPDK_JSONRPC_ERROR_PARSE_ERROR); + return -1; + } + + assert(end != NULL); + + if (conn->values[0].type == SPDK_JSON_VAL_OBJECT_BEGIN) { + parse_single_request(request, conn->values); + } else if (conn->values[0].type == SPDK_JSON_VAL_ARRAY_BEGIN) { + SPDK_DEBUGLOG(SPDK_LOG_RPC, "Got batch array (not currently supported)\n"); + spdk_jsonrpc_server_handle_error(request, SPDK_JSONRPC_ERROR_INVALID_REQUEST); + } else { + SPDK_DEBUGLOG(SPDK_LOG_RPC, "top-level JSON value was not array or object\n"); + spdk_jsonrpc_server_handle_error(request, SPDK_JSONRPC_ERROR_INVALID_REQUEST); + } + + return end - json; +} + +static int +spdk_jsonrpc_server_write_cb(void *cb_ctx, const void *data, size_t size) +{ + struct spdk_jsonrpc_request *request = cb_ctx; + size_t new_size = request->send_buf_size; + + while (new_size - request->send_len < size) { + if (new_size >= SPDK_JSONRPC_SEND_BUF_SIZE_MAX) { + SPDK_ERRLOG("Send buf exceeded maximum size (%zu)\n", + (size_t)SPDK_JSONRPC_SEND_BUF_SIZE_MAX); + return -1; + } + + new_size *= 2; + } + + if (new_size != request->send_buf_size) { + uint8_t *new_buf; + + new_buf = realloc(request->send_buf, new_size); + if (new_buf == NULL) { + SPDK_ERRLOG("Resizing send_buf failed (current size %zu, new size %zu)\n", + request->send_buf_size, new_size); + return -1; + } + + request->send_buf = new_buf; + request->send_buf_size = new_size; + } + + memcpy(request->send_buf + request->send_len, data, size); + request->send_len += size; + + return 0; +} + +static struct spdk_json_write_ctx * +begin_response(struct spdk_jsonrpc_request *request) +{ + struct spdk_json_write_ctx *w; + + w = spdk_json_write_begin(spdk_jsonrpc_server_write_cb, request, 0); + if (w == NULL) { + return NULL; + } + + spdk_json_write_object_begin(w); + spdk_json_write_name(w, "jsonrpc"); + spdk_json_write_string(w, "2.0"); + + spdk_json_write_name(w, "id"); + spdk_json_write_val(w, &request->id); + + return w; +} + +static void +skip_response(struct spdk_jsonrpc_request *request) +{ + request->send_len = 0; + spdk_jsonrpc_server_send_response(request); +} + +static void +end_response(struct spdk_jsonrpc_request *request, struct spdk_json_write_ctx *w) +{ + spdk_json_write_object_end(w); + spdk_json_write_end(w); + spdk_jsonrpc_server_write_cb(request, "\n", 1); + spdk_jsonrpc_server_send_response(request); +} + +void +spdk_jsonrpc_free_request(struct spdk_jsonrpc_request *request) +{ + request->conn->outstanding_requests--; + free(request->send_buf); + free(request); +} + +struct spdk_json_write_ctx * +spdk_jsonrpc_begin_result(struct spdk_jsonrpc_request *request) +{ + struct spdk_json_write_ctx *w; + + if (request->id.type == SPDK_JSON_VAL_INVALID) { + /* Notification - no response required */ + skip_response(request); + return NULL; + } + + w = begin_response(request); + if (w == NULL) { + skip_response(request); + return NULL; + } + + spdk_json_write_name(w, "result"); + + return w; +} + +void +spdk_jsonrpc_end_result(struct spdk_jsonrpc_request *request, struct spdk_json_write_ctx *w) +{ + assert(w != NULL); + + end_response(request, w); +} + +void +spdk_jsonrpc_send_error_response(struct spdk_jsonrpc_request *request, + int error_code, const char *msg) +{ + struct spdk_json_write_ctx *w; + + if (request->id.type == SPDK_JSON_VAL_INVALID) { + /* For error responses, if id is missing, explicitly respond with "id": null. */ + request->id.type = SPDK_JSON_VAL_NULL; + } + + w = begin_response(request); + if (w == NULL) { + skip_response(request); + return; + } + + spdk_json_write_name(w, "error"); + spdk_json_write_object_begin(w); + spdk_json_write_name(w, "code"); + spdk_json_write_int32(w, error_code); + spdk_json_write_name(w, "message"); + spdk_json_write_string(w, msg); + spdk_json_write_object_end(w); + + end_response(request, w); +} + +void +spdk_jsonrpc_send_error_response_fmt(struct spdk_jsonrpc_request *request, + int error_code, const char *fmt, ...) +{ + struct spdk_json_write_ctx *w; + va_list args; + + if (request->id.type == SPDK_JSON_VAL_INVALID) { + /* For error responses, if id is missing, explicitly respond with "id": null. */ + request->id.type = SPDK_JSON_VAL_NULL; + } + + w = begin_response(request); + if (w == NULL) { + skip_response(request); + return; + } + + spdk_json_write_name(w, "error"); + spdk_json_write_object_begin(w); + spdk_json_write_name(w, "code"); + spdk_json_write_int32(w, error_code); + spdk_json_write_name(w, "message"); + va_start(args, fmt); + spdk_json_write_string_fmt_v(w, fmt, args); + va_end(args); + spdk_json_write_object_end(w); + + end_response(request, w); +} + +SPDK_LOG_REGISTER_COMPONENT("rpc", SPDK_LOG_RPC) diff --git a/src/spdk/lib/jsonrpc/jsonrpc_server_tcp.c b/src/spdk/lib/jsonrpc/jsonrpc_server_tcp.c new file mode 100644 index 00000000..c69d7483 --- /dev/null +++ b/src/spdk/lib/jsonrpc/jsonrpc_server_tcp.c @@ -0,0 +1,394 @@ +/*- + * BSD LICENSE + * + * Copyright (c) Intel Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "jsonrpc_internal.h" +#include "spdk/string.h" + +struct spdk_jsonrpc_server * +spdk_jsonrpc_server_listen(int domain, int protocol, + struct sockaddr *listen_addr, socklen_t addrlen, + spdk_jsonrpc_handle_request_fn handle_request) +{ + struct spdk_jsonrpc_server *server; + int rc, val, flag, i; + + server = calloc(1, sizeof(struct spdk_jsonrpc_server)); + if (server == NULL) { + return NULL; + } + + TAILQ_INIT(&server->free_conns); + TAILQ_INIT(&server->conns); + + for (i = 0; i < SPDK_JSONRPC_MAX_CONNS; i++) { + TAILQ_INSERT_TAIL(&server->free_conns, &server->conns_array[i], link); + } + + server->handle_request = handle_request; + + server->sockfd = socket(domain, SOCK_STREAM, protocol); + if (server->sockfd < 0) { + SPDK_ERRLOG("socket() failed\n"); + free(server); + return NULL; + } + + val = 1; + setsockopt(server->sockfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)); + if (protocol == IPPROTO_TCP) { + setsockopt(server->sockfd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)); + } + + flag = fcntl(server->sockfd, F_GETFL); + if (fcntl(server->sockfd, F_SETFL, flag | O_NONBLOCK) < 0) { + SPDK_ERRLOG("fcntl can't set nonblocking mode for socket, fd: %d (%s)\n", + server->sockfd, spdk_strerror(errno)); + close(server->sockfd); + free(server); + return NULL; + } + + rc = bind(server->sockfd, listen_addr, addrlen); + if (rc != 0) { + SPDK_ERRLOG("could not bind JSON-RPC server: %s\n", spdk_strerror(errno)); + close(server->sockfd); + free(server); + return NULL; + } + + rc = listen(server->sockfd, 512); + if (rc != 0) { + SPDK_ERRLOG("listen() failed, errno = %d\n", errno); + close(server->sockfd); + free(server); + return NULL; + } + + return server; +} + +void +spdk_jsonrpc_server_shutdown(struct spdk_jsonrpc_server *server) +{ + struct spdk_jsonrpc_server_conn *conn; + + close(server->sockfd); + + TAILQ_FOREACH(conn, &server->conns, link) { + close(conn->sockfd); + } + + free(server); +} + +static void +spdk_jsonrpc_server_conn_close(struct spdk_jsonrpc_server_conn *conn) +{ + conn->closed = true; + + if (conn->sockfd >= 0) { + close(conn->sockfd); + conn->sockfd = -1; + } +} + +static void +spdk_jsonrpc_server_conn_remove(struct spdk_jsonrpc_server_conn *conn) +{ + struct spdk_jsonrpc_server *server = conn->server; + + spdk_jsonrpc_server_conn_close(conn); + + pthread_spin_destroy(&conn->queue_lock); + assert(STAILQ_EMPTY(&conn->send_queue)); + + TAILQ_REMOVE(&server->conns, conn, link); + TAILQ_INSERT_HEAD(&server->free_conns, conn, link); +} + +static int +spdk_jsonrpc_server_accept(struct spdk_jsonrpc_server *server) +{ + struct spdk_jsonrpc_server_conn *conn; + int rc, flag; + + rc = accept(server->sockfd, NULL, NULL); + if (rc >= 0) { + conn = TAILQ_FIRST(&server->free_conns); + assert(conn != NULL); + + conn->server = server; + conn->sockfd = rc; + conn->closed = false; + conn->recv_len = 0; + conn->outstanding_requests = 0; + pthread_spin_init(&conn->queue_lock, PTHREAD_PROCESS_PRIVATE); + STAILQ_INIT(&conn->send_queue); + conn->send_request = NULL; + + flag = fcntl(conn->sockfd, F_GETFL); + if (fcntl(conn->sockfd, F_SETFL, flag | O_NONBLOCK) < 0) { + SPDK_ERRLOG("fcntl can't set nonblocking mode for socket, fd: %d (%s)\n", + conn->sockfd, spdk_strerror(errno)); + close(conn->sockfd); + return -1; + } + + TAILQ_REMOVE(&server->free_conns, conn, link); + TAILQ_INSERT_TAIL(&server->conns, conn, link); + return 0; + } + + if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) { + return 0; + } + + return -1; +} + +void +spdk_jsonrpc_server_handle_request(struct spdk_jsonrpc_request *request, + const struct spdk_json_val *method, const struct spdk_json_val *params) +{ + request->conn->server->handle_request(request, method, params); +} + +void +spdk_jsonrpc_server_handle_error(struct spdk_jsonrpc_request *request, int error) +{ + const char *msg; + + switch (error) { + case SPDK_JSONRPC_ERROR_PARSE_ERROR: + msg = "Parse error"; + break; + + case SPDK_JSONRPC_ERROR_INVALID_REQUEST: + msg = "Invalid request"; + break; + + case SPDK_JSONRPC_ERROR_METHOD_NOT_FOUND: + msg = "Method not found"; + break; + + case SPDK_JSONRPC_ERROR_INVALID_PARAMS: + msg = "Invalid parameters"; + break; + + case SPDK_JSONRPC_ERROR_INTERNAL_ERROR: + msg = "Internal error"; + break; + + default: + msg = "Error"; + break; + } + + spdk_jsonrpc_send_error_response(request, error, msg); +} + +static int +spdk_jsonrpc_server_conn_recv(struct spdk_jsonrpc_server_conn *conn) +{ + ssize_t rc; + size_t recv_avail = SPDK_JSONRPC_RECV_BUF_SIZE - conn->recv_len; + + rc = recv(conn->sockfd, conn->recv_buf + conn->recv_len, recv_avail, 0); + if (rc == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) { + return 0; + } + SPDK_DEBUGLOG(SPDK_LOG_RPC, "recv() failed: %s\n", spdk_strerror(errno)); + return -1; + } + + if (rc == 0) { + SPDK_DEBUGLOG(SPDK_LOG_RPC, "remote closed connection\n"); + return -1; + } + + conn->recv_len += rc; + + rc = spdk_jsonrpc_parse_request(conn, conn->recv_buf, conn->recv_len); + if (rc < 0) { + SPDK_ERRLOG("jsonrpc parse request failed\n"); + return -1; + } + + if (rc > 0) { + /* + * Successfully parsed a request - move any data past the end of the + * parsed request down to the beginning. + */ + assert((size_t)rc <= conn->recv_len); + memmove(conn->recv_buf, conn->recv_buf + rc, conn->recv_len - rc); + conn->recv_len -= rc; + } + + return 0; +} + +void +spdk_jsonrpc_server_send_response(struct spdk_jsonrpc_request *request) +{ + struct spdk_jsonrpc_server_conn *conn = request->conn; + + /* Queue the response to be sent */ + pthread_spin_lock(&conn->queue_lock); + STAILQ_INSERT_TAIL(&conn->send_queue, request, link); + pthread_spin_unlock(&conn->queue_lock); +} + +static struct spdk_jsonrpc_request * +spdk_jsonrpc_server_dequeue_request(struct spdk_jsonrpc_server_conn *conn) +{ + struct spdk_jsonrpc_request *request = NULL; + + pthread_spin_lock(&conn->queue_lock); + request = STAILQ_FIRST(&conn->send_queue); + if (request) { + STAILQ_REMOVE_HEAD(&conn->send_queue, link); + } + pthread_spin_unlock(&conn->queue_lock); + return request; +} + + +static int +spdk_jsonrpc_server_conn_send(struct spdk_jsonrpc_server_conn *conn) +{ + struct spdk_jsonrpc_request *request; + ssize_t rc; + +more: + if (conn->outstanding_requests == 0) { + return 0; + } + + if (conn->send_request == NULL) { + conn->send_request = spdk_jsonrpc_server_dequeue_request(conn); + } + + request = conn->send_request; + if (request == NULL) { + /* Nothing to send right now */ + return 0; + } + + if (request->send_len > 0) { + rc = send(conn->sockfd, request->send_buf + request->send_offset, + request->send_len, 0); + if (rc < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) { + return 0; + } + + SPDK_DEBUGLOG(SPDK_LOG_RPC, "send() failed: %s\n", spdk_strerror(errno)); + return -1; + } + + request->send_offset += rc; + request->send_len -= rc; + } + + if (request->send_len == 0) { + /* + * Full response has been sent. + * Free it and set send_request to NULL to move on to the next queued response. + */ + conn->send_request = NULL; + spdk_jsonrpc_free_request(request); + goto more; + } + + return 0; +} + +int +spdk_jsonrpc_server_poll(struct spdk_jsonrpc_server *server) +{ + int rc; + struct spdk_jsonrpc_server_conn *conn, *conn_tmp; + + TAILQ_FOREACH_SAFE(conn, &server->conns, link, conn_tmp) { + if (conn->closed) { + struct spdk_jsonrpc_request *request; + + /* + * The client closed the connection, but there may still be requests + * outstanding; we have no way to cancel outstanding requests, so wait until + * each outstanding request sends a response (which will be discarded, since + * the connection is closed). + */ + + if (conn->send_request) { + spdk_jsonrpc_free_request(conn->send_request); + conn->send_request = NULL; + } + + while ((request = spdk_jsonrpc_server_dequeue_request(conn)) != NULL) { + spdk_jsonrpc_free_request(request); + } + + if (conn->outstanding_requests == 0) { + SPDK_DEBUGLOG(SPDK_LOG_RPC, "all outstanding requests completed\n"); + spdk_jsonrpc_server_conn_remove(conn); + } + } + } + + /* Check listen socket */ + if (!TAILQ_EMPTY(&server->free_conns)) { + spdk_jsonrpc_server_accept(server); + } + + TAILQ_FOREACH(conn, &server->conns, link) { + if (conn->closed) { + continue; + } + + rc = spdk_jsonrpc_server_conn_send(conn); + if (rc != 0) { + spdk_jsonrpc_server_conn_close(conn); + continue; + } + + rc = spdk_jsonrpc_server_conn_recv(conn); + if (rc != 0) { + spdk_jsonrpc_server_conn_close(conn); + continue; + } + } + + return 0; +} |