diff options
Diffstat (limited to 'src/spdk/lib/jsonrpc/jsonrpc_client_tcp.c')
-rw-r--r-- | src/spdk/lib/jsonrpc/jsonrpc_client_tcp.c | 284 |
1 files changed, 284 insertions, 0 deletions
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; +} |