Spdk/lib/jsonrpc/jsonrpc_client_tcp.c
paul luse a6dbe3721e update Intel copyright notices
per Intel policy to include file commit date using git cmd
below.  The policy does not apply to non-Intel (C) notices.

git log --follow -C90% --format=%ad --date default <file> | tail -1

and then pull just the 4 digit year from the result.

Intel copyrights were not added to files where Intel either had
no contribution ot the contribution lacked substance (ie license
header updates, formatting changes, etc).  Contribution date used
"--follow -C95%" to get the most accurate date.

Note that several files in this patch didn't end the license/(c)
block with a blank comment line so these were added as the vast
majority of files do have this last blank line.  Simply there for
consistency.

Signed-off-by: paul luse <paul.e.luse@intel.com>
Change-Id: Id5b7ce4f658fe87132f14139ead58d6e285c04d4
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/15192
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: Jim Harris <james.r.harris@intel.com>
Reviewed-by: Ben Walker <benjamin.walker@intel.com>
Community-CI: Mellanox Build Bot
2022-11-10 08:28:53 +00:00

398 lines
8.5 KiB
C

/* SPDX-License-Identifier: BSD-3-Clause
* Copyright (C) 2018 Intel Corporation.
* All rights reserved.
*/
#include "spdk/string.h"
#include "jsonrpc_internal.h"
#include "spdk/util.h"
#define RPC_DEFAULT_PORT "5260"
static int
jsonrpc_client_send_request(struct spdk_jsonrpc_client *client)
{
ssize_t rc;
struct spdk_jsonrpc_client_request *request = client->request;
if (!request) {
return 0;
}
if (request->send_len > 0) {
rc = send(client->sockfd, request->send_buf + request->send_offset,
request->send_len, 0);
if (rc < 0) {
/* For EINTR we pretend that nothing was send. */
if (errno == EINTR) {
rc = 0;
} else {
rc = -errno;
SPDK_ERRLOG("poll() failed (%d): %s\n", errno, spdk_strerror(errno));
}
return rc;
}
request->send_offset += rc;
request->send_len -= rc;
}
if (request->send_len == 0) {
client->request = NULL;
spdk_jsonrpc_client_free_request(request);
}
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;
}
static int
jsonrpc_client_resp_ready_count(struct spdk_jsonrpc_client *client)
{
return client->resp != NULL && client->resp->ready ? 1 : 0;
}
static int
jsonrpc_client_recv(struct spdk_jsonrpc_client *client)
{
ssize_t rc;
if (client->recv_buf == NULL) {
client->recv_buf = malloc(SPDK_JSONRPC_SEND_BUF_SIZE_INIT);
if (!client->recv_buf) {
rc = errno;
SPDK_ERRLOG("malloc() failed (%d): %s\n", (int)rc, spdk_strerror(rc));
return -rc;
}
client->recv_buf_size = SPDK_JSONRPC_SEND_BUF_SIZE_INIT;
client->recv_offset = 0;
} else if (client->recv_offset == client->recv_buf_size - 1) {
rc = recv_buf_expand(client);
if (rc) {
return rc;
}
}
rc = recv(client->sockfd, client->recv_buf + client->recv_offset,
client->recv_buf_size - client->recv_offset - 1, 0);
if (rc < 0) {
/* For EINTR we pretend that nothing was received. */
if (errno == EINTR) {
return 0;
} else {
rc = -errno;
SPDK_ERRLOG("recv() failed (%d): %s\n", errno, spdk_strerror(errno));
return rc;
}
} else if (rc == 0) {
return -EIO;
}
client->recv_offset += rc;
client->recv_buf[client->recv_offset] = '\0';
/* Check to see if we have received a full JSON value. */
return jsonrpc_parse_response(client);
}
static int
jsonrpc_client_poll(struct spdk_jsonrpc_client *client, int timeout)
{
int rc;
struct pollfd pfd = { .fd = client->sockfd, .events = POLLIN | POLLOUT };
rc = poll(&pfd, 1, timeout);
if (rc == -1) {
if (errno == EINTR) {
/* For EINTR we pretend that nothing was received nor send. */
rc = 0;
} else {
rc = -errno;
SPDK_ERRLOG("poll() failed (%d): %s\n", errno, spdk_strerror(errno));
}
} else if (rc > 0) {
rc = 0;
if (pfd.revents & POLLOUT) {
rc = jsonrpc_client_send_request(client);
}
if (rc == 0 && (pfd.revents & POLLIN)) {
rc = jsonrpc_client_recv(client);
/* Incomplete message in buffer isn't an error. */
if (rc == -EAGAIN) {
rc = 0;
}
}
}
return rc ? rc : jsonrpc_client_resp_ready_count(client);
}
static int
jsonrpc_client_poll_connecting(struct spdk_jsonrpc_client *client, int timeout)
{
socklen_t rc_len;
int rc;
struct pollfd pfd = {
.fd = client->sockfd,
.events = POLLOUT
};
rc = poll(&pfd, 1, timeout);
if (rc == 0) {
return -ENOTCONN;
} else if (rc == -1) {
if (errno != EINTR) {
SPDK_ERRLOG("poll() failed (%d): %s\n", errno, spdk_strerror(errno));
goto err;
}
/* We are still not connected. Caller will have to call us again. */
return -ENOTCONN;
} else if (pfd.revents & ~POLLOUT) {
/* We only poll for POLLOUT */
goto err;
} else if ((pfd.revents & POLLOUT) == 0) {
/* Is this even possible to get here? */
return -ENOTCONN;
}
rc_len = sizeof(int);
/* connection might fail so need to check SO_ERROR. */
if (getsockopt(client->sockfd, SOL_SOCKET, SO_ERROR, &rc, &rc_len) == -1) {
goto err;
}
if (rc == 0) {
client->connected = true;
return 0;
}
err:
return -EIO;
}
static int
jsonrpc_client_connect(struct spdk_jsonrpc_client *client, int domain, int protocol,
struct sockaddr *server_addr, socklen_t addrlen)
{
int rc;
client->sockfd = socket(domain, SOCK_STREAM | SOCK_NONBLOCK, protocol);
if (client->sockfd < 0) {
rc = errno;
SPDK_ERRLOG("socket() failed\n");
return -rc;
}
rc = connect(client->sockfd, server_addr, addrlen);
if (rc != 0) {
rc = errno;
if (rc != EINPROGRESS) {
SPDK_ERRLOG("could not connect to JSON-RPC server: %s\n", spdk_strerror(errno));
goto err;
}
} else {
client->connected = true;
}
return -rc;
err:
close(client->sockfd);
client->sockfd = -1;
return -rc;
}
struct spdk_jsonrpc_client *
spdk_jsonrpc_client_connect(const char *addr, int addr_family)
{
struct spdk_jsonrpc_client *client = calloc(1, sizeof(struct spdk_jsonrpc_client));
/* Unix Domain Socket */
struct sockaddr_un addr_un = {};
char *add_in = NULL;
int rc;
if (client == NULL) {
SPDK_ERRLOG("%s\n", spdk_strerror(errno));
return NULL;
}
if (addr_family == AF_UNIX) {
addr_un.sun_family = AF_UNIX;
rc = snprintf(addr_un.sun_path, sizeof(addr_un.sun_path), "%s", addr);
if (rc < 0 || (size_t)rc >= sizeof(addr_un.sun_path)) {
rc = -EINVAL;
SPDK_ERRLOG("RPC Listen address Unix socket path too long\n");
goto err;
}
rc = jsonrpc_client_connect(client, AF_UNIX, 0, (struct sockaddr *)&addr_un, sizeof(addr_un));
} else {
/* TCP/IP socket */
struct addrinfo hints;
struct addrinfo *res;
char *host, *port;
add_in = strdup(addr);
if (!add_in) {
rc = -errno;
SPDK_ERRLOG("%s\n", spdk_strerror(errno));
goto err;
}
rc = spdk_parse_ip_addr(add_in, &host, &port);
if (rc) {
SPDK_ERRLOG("Invalid listen address '%s'\n", addr);
goto err;
}
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;
rc = getaddrinfo(host, port, &hints, &res);
if (rc != 0) {
SPDK_ERRLOG("Unable to look up RPC connect address '%s' (%d): %s\n", addr, rc, gai_strerror(rc));
rc = -EINVAL;
goto err;
}
rc = jsonrpc_client_connect(client, res->ai_family, res->ai_protocol, res->ai_addr,
res->ai_addrlen);
freeaddrinfo(res);
}
err:
if (rc != 0 && rc != -EINPROGRESS) {
free(client);
client = NULL;
errno = -rc;
}
free(add_in);
return client;
}
void
spdk_jsonrpc_client_close(struct spdk_jsonrpc_client *client)
{
if (client->sockfd >= 0) {
close(client->sockfd);
}
free(client->recv_buf);
if (client->resp) {
spdk_jsonrpc_client_free_response(&client->resp->jsonrpc);
}
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_poll(struct spdk_jsonrpc_client *client, int timeout)
{
if (client->connected) {
return jsonrpc_client_poll(client, timeout);
} else {
return jsonrpc_client_poll_connecting(client, timeout);
}
}
int
spdk_jsonrpc_client_send_request(struct spdk_jsonrpc_client *client,
struct spdk_jsonrpc_client_request *req)
{
if (client->request != NULL) {
return -ENOSPC;
}
client->request = req;
return 0;
}
struct spdk_jsonrpc_client_response *
spdk_jsonrpc_client_get_response(struct spdk_jsonrpc_client *client)
{
struct spdk_jsonrpc_client_response_internal *r;
r = client->resp;
if (r == NULL || r->ready == false) {
return NULL;
}
client->resp = NULL;
return &r->jsonrpc;
}
void
spdk_jsonrpc_client_free_response(struct spdk_jsonrpc_client_response *resp)
{
struct spdk_jsonrpc_client_response_internal *r;
if (!resp) {
return;
}
r = SPDK_CONTAINEROF(resp, struct spdk_jsonrpc_client_response_internal, jsonrpc);
free(r->buf);
free(r);
}