2009-10-26 15:04:05 -07:00
|
|
|
|
/*
|
2018-01-22 11:09:40 -08:00
|
|
|
|
* Copyright (c) 2009-2017 Nicira, Inc.
|
2009-10-26 15:04:05 -07:00
|
|
|
|
*
|
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
|
* You may obtain a copy of the License at:
|
|
|
|
|
*
|
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
*
|
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
|
* limitations under the License.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
|
|
#include "jsonrpc.h"
|
|
|
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
|
|
|
|
|
#include "byteq.h"
|
2025-04-08 09:57:31 +02:00
|
|
|
|
#include "coverage.h"
|
2016-03-03 10:20:46 -08:00
|
|
|
|
#include "openvswitch/dynamic-string.h"
|
2010-04-13 09:28:13 -07:00
|
|
|
|
#include "fatal-signal.h"
|
2016-07-12 16:37:34 -05:00
|
|
|
|
#include "openvswitch/json.h"
|
2016-03-25 14:10:21 -07:00
|
|
|
|
#include "openvswitch/list.h"
|
2016-03-25 14:10:24 -07:00
|
|
|
|
#include "openvswitch/ofpbuf.h"
|
2021-05-27 15:29:04 +02:00
|
|
|
|
#include "ovs-replay.h"
|
2013-04-15 14:35:18 -07:00
|
|
|
|
#include "ovs-thread.h"
|
2017-11-03 13:53:53 +08:00
|
|
|
|
#include "openvswitch/poll-loop.h"
|
2009-12-02 10:50:18 -08:00
|
|
|
|
#include "reconnect.h"
|
2009-10-26 15:04:05 -07:00
|
|
|
|
#include "stream.h"
|
2018-01-22 11:09:40 -08:00
|
|
|
|
#include "svec.h"
|
2009-12-02 10:50:18 -08:00
|
|
|
|
#include "timeval.h"
|
2014-12-15 14:10:38 +01:00
|
|
|
|
#include "openvswitch/vlog.h"
|
2010-07-16 11:02:49 -07:00
|
|
|
|
|
2010-10-19 14:47:01 -07:00
|
|
|
|
VLOG_DEFINE_THIS_MODULE(jsonrpc);
|
2025-04-08 09:57:31 +02:00
|
|
|
|
|
|
|
|
|
COVERAGE_DEFINE(jsonrpc_recv_incomplete);
|
2009-10-26 15:04:05 -07:00
|
|
|
|
|
|
|
|
|
struct jsonrpc {
|
|
|
|
|
struct stream *stream;
|
|
|
|
|
char *name;
|
|
|
|
|
int status;
|
|
|
|
|
|
|
|
|
|
/* Input. */
|
|
|
|
|
struct byteq input;
|
2019-11-06 11:19:44 +02:00
|
|
|
|
uint8_t input_buffer[4096];
|
2009-10-26 15:04:05 -07:00
|
|
|
|
struct json_parser *parser;
|
|
|
|
|
|
|
|
|
|
/* Output. */
|
2014-12-15 14:10:38 +01:00
|
|
|
|
struct ovs_list output; /* Contains "struct ofpbuf"s. */
|
2014-09-18 10:49:47 -07:00
|
|
|
|
size_t output_count; /* Number of elements in "output". */
|
2009-10-26 15:04:05 -07:00
|
|
|
|
size_t backlog;
|
2020-10-21 03:32:49 +02:00
|
|
|
|
|
|
|
|
|
/* Limits. */
|
|
|
|
|
size_t max_output; /* 'output_count' disconnection threshold. */
|
|
|
|
|
size_t max_backlog; /* 'backlog' disconnection threshold. */
|
2009-10-26 15:04:05 -07:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* Rate limit for error messages. */
|
|
|
|
|
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5);
|
|
|
|
|
|
2014-04-03 15:27:18 -07:00
|
|
|
|
static struct jsonrpc_msg *jsonrpc_parse_received_message(struct jsonrpc *);
|
2009-10-26 15:04:05 -07:00
|
|
|
|
static void jsonrpc_cleanup(struct jsonrpc *);
|
2012-02-17 16:47:36 -08:00
|
|
|
|
static void jsonrpc_error(struct jsonrpc *, int error);
|
2009-10-26 15:04:05 -07:00
|
|
|
|
|
2010-03-18 12:59:34 -07:00
|
|
|
|
/* This is just the same as stream_open() except that it uses the default
|
2013-09-23 14:33:09 -07:00
|
|
|
|
* JSONRPC port if none is specified. */
|
2010-03-18 12:59:34 -07:00
|
|
|
|
int
|
2012-03-10 15:58:10 -08:00
|
|
|
|
jsonrpc_stream_open(const char *name, struct stream **streamp, uint8_t dscp)
|
2010-03-18 12:59:34 -07:00
|
|
|
|
{
|
2015-03-11 13:32:01 -07:00
|
|
|
|
return stream_open_with_default_port(name, OVSDB_PORT, streamp, dscp);
|
2010-03-18 12:59:34 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* This is just the same as pstream_open() except that it uses the default
|
2013-09-23 14:33:09 -07:00
|
|
|
|
* JSONRPC port if none is specified. */
|
2010-03-18 12:59:34 -07:00
|
|
|
|
int
|
2012-03-10 15:58:10 -08:00
|
|
|
|
jsonrpc_pstream_open(const char *name, struct pstream **pstreamp, uint8_t dscp)
|
2010-03-18 12:59:34 -07:00
|
|
|
|
{
|
2015-03-11 13:32:01 -07:00
|
|
|
|
return pstream_open_with_default_port(name, OVSDB_PORT, pstreamp, dscp);
|
2010-03-18 12:59:34 -07:00
|
|
|
|
}
|
|
|
|
|
|
2012-02-27 09:57:36 -08:00
|
|
|
|
/* Returns a new JSON-RPC stream that uses 'stream' for input and output. The
|
|
|
|
|
* new jsonrpc object takes ownership of 'stream'. */
|
2009-10-26 15:04:05 -07:00
|
|
|
|
struct jsonrpc *
|
|
|
|
|
jsonrpc_open(struct stream *stream)
|
|
|
|
|
{
|
|
|
|
|
struct jsonrpc *rpc;
|
|
|
|
|
|
2012-11-06 13:14:55 -08:00
|
|
|
|
ovs_assert(stream != NULL);
|
2009-10-26 15:04:05 -07:00
|
|
|
|
|
|
|
|
|
rpc = xzalloc(sizeof *rpc);
|
|
|
|
|
rpc->name = xstrdup(stream_get_name(stream));
|
|
|
|
|
rpc->stream = stream;
|
2013-04-25 10:48:03 -07:00
|
|
|
|
byteq_init(&rpc->input, rpc->input_buffer, sizeof rpc->input_buffer);
|
2016-03-25 14:10:22 -07:00
|
|
|
|
ovs_list_init(&rpc->output);
|
2009-10-26 15:04:05 -07:00
|
|
|
|
|
|
|
|
|
return rpc;
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-27 09:57:36 -08:00
|
|
|
|
/* Destroys 'rpc', closing the stream on which it is based, and frees its
|
|
|
|
|
* memory. */
|
2009-10-26 15:04:05 -07:00
|
|
|
|
void
|
|
|
|
|
jsonrpc_close(struct jsonrpc *rpc)
|
|
|
|
|
{
|
|
|
|
|
if (rpc) {
|
|
|
|
|
jsonrpc_cleanup(rpc);
|
|
|
|
|
free(rpc->name);
|
|
|
|
|
free(rpc);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-27 09:57:36 -08:00
|
|
|
|
/* Performs periodic maintenance on 'rpc', such as flushing output buffers. */
|
2009-10-26 15:04:05 -07:00
|
|
|
|
void
|
|
|
|
|
jsonrpc_run(struct jsonrpc *rpc)
|
|
|
|
|
{
|
|
|
|
|
if (rpc->status) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-06 14:26:48 -08:00
|
|
|
|
stream_run(rpc->stream);
|
2016-03-25 14:10:22 -07:00
|
|
|
|
while (!ovs_list_is_empty(&rpc->output)) {
|
2024-09-09 00:55:00 -04:00
|
|
|
|
struct ovs_list *head = ovs_list_front(&rpc->output);
|
|
|
|
|
struct ofpbuf *buf = ofpbuf_from_list(head);
|
2009-10-26 15:04:05 -07:00
|
|
|
|
int retval;
|
|
|
|
|
|
2015-03-02 17:29:44 -08:00
|
|
|
|
retval = stream_send(rpc->stream, buf->data, buf->size);
|
2009-10-26 15:04:05 -07:00
|
|
|
|
if (retval >= 0) {
|
|
|
|
|
rpc->backlog -= retval;
|
|
|
|
|
ofpbuf_pull(buf, retval);
|
2015-03-02 17:29:44 -08:00
|
|
|
|
if (!buf->size) {
|
2024-09-09 00:55:00 -04:00
|
|
|
|
ovs_list_remove(head);
|
2014-09-18 10:49:47 -07:00
|
|
|
|
rpc->output_count--;
|
2010-12-06 10:03:31 -08:00
|
|
|
|
ofpbuf_delete(buf);
|
2009-10-26 15:04:05 -07:00
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (retval != -EAGAIN) {
|
|
|
|
|
VLOG_WARN_RL(&rl, "%s: send error: %s",
|
2013-06-24 10:54:49 -07:00
|
|
|
|
rpc->name, ovs_strerror(-retval));
|
2009-10-26 15:04:05 -07:00
|
|
|
|
jsonrpc_error(rpc, -retval);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-27 09:57:36 -08:00
|
|
|
|
/* Arranges for the poll loop to wake up when 'rpc' needs to perform
|
|
|
|
|
* maintenance activities. */
|
2009-10-26 15:04:05 -07:00
|
|
|
|
void
|
|
|
|
|
jsonrpc_wait(struct jsonrpc *rpc)
|
|
|
|
|
{
|
2010-01-06 14:26:48 -08:00
|
|
|
|
if (!rpc->status) {
|
|
|
|
|
stream_run_wait(rpc->stream);
|
2016-03-25 14:10:22 -07:00
|
|
|
|
if (!ovs_list_is_empty(&rpc->output)) {
|
2010-01-06 14:26:48 -08:00
|
|
|
|
stream_send_wait(rpc->stream);
|
|
|
|
|
}
|
2009-10-26 15:04:05 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-01-30 11:29:14 -08:00
|
|
|
|
/*
|
2012-02-27 09:57:36 -08:00
|
|
|
|
* Returns the current status of 'rpc'. The possible return values are:
|
2011-01-30 11:29:14 -08:00
|
|
|
|
* - 0: no error yet
|
|
|
|
|
* - >0: errno value
|
2012-02-27 09:57:36 -08:00
|
|
|
|
* - EOF: end of file (remote end closed connection; not necessarily an error).
|
|
|
|
|
*
|
2016-08-11 10:31:43 -07:00
|
|
|
|
* When this function returns nonzero, 'rpc' is effectively out of
|
|
|
|
|
* commission. 'rpc' will not receive any more messages and any further
|
|
|
|
|
* messages that one attempts to send with 'rpc' will be discarded. The
|
|
|
|
|
* caller can keep 'rpc' around as long as it wants, but it's not going
|
|
|
|
|
* to provide any more useful services.
|
2011-01-30 11:29:14 -08:00
|
|
|
|
*/
|
2009-10-26 15:04:05 -07:00
|
|
|
|
int
|
|
|
|
|
jsonrpc_get_status(const struct jsonrpc *rpc)
|
|
|
|
|
{
|
|
|
|
|
return rpc->status;
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-27 09:57:36 -08:00
|
|
|
|
/* Returns the number of bytes buffered by 'rpc' to be written to the
|
|
|
|
|
* underlying stream. Always returns 0 if 'rpc' has encountered an error or if
|
|
|
|
|
* the remote end closed the connection. */
|
2009-10-26 15:04:05 -07:00
|
|
|
|
size_t
|
|
|
|
|
jsonrpc_get_backlog(const struct jsonrpc *rpc)
|
|
|
|
|
{
|
|
|
|
|
return rpc->status ? 0 : rpc->backlog;
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-21 03:32:49 +02:00
|
|
|
|
/* Sets thresholds for send backlog. If send backlog contains more than
|
|
|
|
|
* 'max_n_msgs' messages or is larger than 'max_backlog_bytes' bytes,
|
|
|
|
|
* connection will be dropped. */
|
|
|
|
|
void
|
|
|
|
|
jsonrpc_set_backlog_threshold(struct jsonrpc *rpc,
|
|
|
|
|
size_t max_n_msgs, size_t max_backlog_bytes)
|
|
|
|
|
{
|
|
|
|
|
rpc->max_output = max_n_msgs;
|
|
|
|
|
rpc->max_backlog = max_backlog_bytes;
|
|
|
|
|
}
|
|
|
|
|
|
2012-09-05 13:34:35 -07:00
|
|
|
|
/* Returns the number of bytes that have been received on 'rpc''s underlying
|
|
|
|
|
* stream. (The value wraps around if it exceeds UINT_MAX.) */
|
|
|
|
|
unsigned int
|
|
|
|
|
jsonrpc_get_received_bytes(const struct jsonrpc *rpc)
|
|
|
|
|
{
|
|
|
|
|
return rpc->input.head;
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-27 09:57:36 -08:00
|
|
|
|
/* Returns 'rpc''s name, that is, the name returned by stream_get_name() for
|
|
|
|
|
* the stream underlying 'rpc' when 'rpc' was created. */
|
2009-10-26 15:04:05 -07:00
|
|
|
|
const char *
|
|
|
|
|
jsonrpc_get_name(const struct jsonrpc *rpc)
|
|
|
|
|
{
|
|
|
|
|
return rpc->name;
|
|
|
|
|
}
|
|
|
|
|
|
2009-11-16 16:55:35 -08:00
|
|
|
|
static void
|
|
|
|
|
jsonrpc_log_msg(const struct jsonrpc *rpc, const char *title,
|
|
|
|
|
const struct jsonrpc_msg *msg)
|
|
|
|
|
{
|
|
|
|
|
if (VLOG_IS_DBG_ENABLED()) {
|
|
|
|
|
struct ds s = DS_EMPTY_INITIALIZER;
|
|
|
|
|
if (msg->method) {
|
|
|
|
|
ds_put_format(&s, ", method=\"%s\"", msg->method);
|
|
|
|
|
}
|
|
|
|
|
if (msg->params) {
|
|
|
|
|
ds_put_cstr(&s, ", params=");
|
2023-12-14 02:04:05 +01:00
|
|
|
|
json_to_ds(msg->params, JSSF_SORT, &s);
|
2009-11-16 16:55:35 -08:00
|
|
|
|
}
|
|
|
|
|
if (msg->result) {
|
|
|
|
|
ds_put_cstr(&s, ", result=");
|
2023-12-14 02:04:05 +01:00
|
|
|
|
json_to_ds(msg->result, JSSF_SORT, &s);
|
2009-11-16 16:55:35 -08:00
|
|
|
|
}
|
|
|
|
|
if (msg->error) {
|
|
|
|
|
ds_put_cstr(&s, ", error=");
|
2023-12-14 02:04:05 +01:00
|
|
|
|
json_to_ds(msg->error, JSSF_SORT, &s);
|
2009-11-16 16:55:35 -08:00
|
|
|
|
}
|
|
|
|
|
if (msg->id) {
|
|
|
|
|
ds_put_cstr(&s, ", id=");
|
2023-12-14 02:04:05 +01:00
|
|
|
|
json_to_ds(msg->id, JSSF_SORT, &s);
|
2009-11-16 16:55:35 -08:00
|
|
|
|
}
|
|
|
|
|
VLOG_DBG("%s: %s %s%s", rpc->name, title,
|
|
|
|
|
jsonrpc_msg_type_to_string(msg->type), ds_cstr(&s));
|
|
|
|
|
ds_destroy(&s);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-27 09:57:36 -08:00
|
|
|
|
/* Schedules 'msg' to be sent on 'rpc' and returns 'rpc''s status (as with
|
|
|
|
|
* jsonrpc_get_status()).
|
|
|
|
|
*
|
|
|
|
|
* If 'msg' cannot be sent immediately, it is appended to a buffer. The caller
|
|
|
|
|
* is responsible for ensuring that the amount of buffered data is somehow
|
|
|
|
|
* limited. (jsonrpc_get_backlog() returns the amount of data currently
|
|
|
|
|
* buffered in 'rpc'.)
|
|
|
|
|
*
|
|
|
|
|
* Always takes ownership of 'msg', regardless of success. */
|
2009-10-26 15:04:05 -07:00
|
|
|
|
int
|
|
|
|
|
jsonrpc_send(struct jsonrpc *rpc, struct jsonrpc_msg *msg)
|
|
|
|
|
{
|
|
|
|
|
struct ofpbuf *buf;
|
|
|
|
|
struct json *json;
|
2015-08-11 14:11:58 -07:00
|
|
|
|
struct ds ds = DS_EMPTY_INITIALIZER;
|
2009-10-26 15:04:05 -07:00
|
|
|
|
size_t length;
|
|
|
|
|
|
|
|
|
|
if (rpc->status) {
|
|
|
|
|
jsonrpc_msg_destroy(msg);
|
|
|
|
|
return rpc->status;
|
|
|
|
|
}
|
|
|
|
|
|
2009-11-16 16:55:35 -08:00
|
|
|
|
jsonrpc_log_msg(rpc, "send", msg);
|
|
|
|
|
|
2009-10-26 15:04:05 -07:00
|
|
|
|
json = jsonrpc_msg_to_json(msg);
|
2015-08-11 14:11:58 -07:00
|
|
|
|
json_to_ds(json, 0, &ds);
|
|
|
|
|
length = ds.length;
|
2009-10-26 15:04:05 -07:00
|
|
|
|
json_destroy(json);
|
|
|
|
|
|
|
|
|
|
buf = xmalloc(sizeof *buf);
|
2015-08-11 14:14:59 -07:00
|
|
|
|
ofpbuf_use_ds(buf, &ds);
|
2016-03-25 14:10:22 -07:00
|
|
|
|
ovs_list_push_back(&rpc->output, &buf->list_node);
|
2014-09-18 10:49:47 -07:00
|
|
|
|
rpc->output_count++;
|
2009-10-26 15:04:05 -07:00
|
|
|
|
rpc->backlog += length;
|
|
|
|
|
|
2014-09-18 10:49:47 -07:00
|
|
|
|
if (rpc->output_count >= 50) {
|
2020-10-21 03:32:49 +02:00
|
|
|
|
static struct vlog_rate_limit bl_rl = VLOG_RATE_LIMIT_INIT(5, 5);
|
|
|
|
|
bool disconnect = false;
|
|
|
|
|
|
|
|
|
|
VLOG_INFO_RL(&bl_rl, "excessive sending backlog, jsonrpc: %s, num of"
|
2014-09-18 10:49:47 -07:00
|
|
|
|
" msgs: %"PRIuSIZE", backlog: %"PRIuSIZE".", rpc->name,
|
|
|
|
|
rpc->output_count, rpc->backlog);
|
2020-10-21 03:32:49 +02:00
|
|
|
|
if (rpc->max_output && rpc->output_count > rpc->max_output) {
|
|
|
|
|
disconnect = true;
|
|
|
|
|
VLOG_WARN("sending backlog exceeded maximum number of messages (%"
|
|
|
|
|
PRIuSIZE" > %"PRIuSIZE"), disconnecting, jsonrpc: %s.",
|
|
|
|
|
rpc->output_count, rpc->max_output, rpc->name);
|
|
|
|
|
} else if (rpc->max_backlog && rpc->backlog > rpc->max_backlog) {
|
|
|
|
|
disconnect = true;
|
|
|
|
|
VLOG_WARN("sending backlog exceeded maximum size (%"PRIuSIZE" > %"
|
|
|
|
|
PRIuSIZE" bytes), disconnecting, jsonrpc: %s.",
|
|
|
|
|
rpc->backlog, rpc->max_backlog, rpc->name);
|
|
|
|
|
}
|
|
|
|
|
if (disconnect) {
|
|
|
|
|
jsonrpc_error(rpc, E2BIG);
|
|
|
|
|
}
|
2014-09-18 10:49:47 -07:00
|
|
|
|
}
|
|
|
|
|
|
2010-12-06 10:03:31 -08:00
|
|
|
|
if (rpc->backlog == length) {
|
2009-10-26 15:04:05 -07:00
|
|
|
|
jsonrpc_run(rpc);
|
|
|
|
|
}
|
|
|
|
|
return rpc->status;
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-27 09:57:36 -08:00
|
|
|
|
/* Attempts to receive a message from 'rpc'.
|
|
|
|
|
*
|
|
|
|
|
* If successful, stores the received message in '*msgp' and returns 0. The
|
|
|
|
|
* caller takes ownership of '*msgp' and must eventually destroy it with
|
|
|
|
|
* jsonrpc_msg_destroy().
|
|
|
|
|
*
|
|
|
|
|
* Otherwise, stores NULL in '*msgp' and returns one of the following:
|
|
|
|
|
*
|
|
|
|
|
* - EAGAIN: No message has been received.
|
|
|
|
|
*
|
|
|
|
|
* - EOF: The remote end closed the connection gracefully.
|
|
|
|
|
*
|
|
|
|
|
* - Otherwise an errno value that represents a JSON-RPC protocol violation
|
|
|
|
|
* or another error fatal to the connection. 'rpc' will not send or
|
|
|
|
|
* receive any more messages.
|
|
|
|
|
*/
|
2009-10-26 15:04:05 -07:00
|
|
|
|
int
|
|
|
|
|
jsonrpc_recv(struct jsonrpc *rpc, struct jsonrpc_msg **msgp)
|
|
|
|
|
{
|
2012-04-24 10:57:41 -07:00
|
|
|
|
int i;
|
|
|
|
|
|
2009-10-26 15:04:05 -07:00
|
|
|
|
*msgp = NULL;
|
|
|
|
|
if (rpc->status) {
|
|
|
|
|
return rpc->status;
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-24 10:57:41 -07:00
|
|
|
|
for (i = 0; i < 50; i++) {
|
2014-04-03 15:27:18 -07:00
|
|
|
|
size_t n, used;
|
|
|
|
|
|
|
|
|
|
/* Fill our input buffer if it's empty. */
|
|
|
|
|
if (byteq_is_empty(&rpc->input)) {
|
2009-10-26 15:04:05 -07:00
|
|
|
|
size_t chunk;
|
|
|
|
|
int retval;
|
|
|
|
|
|
2025-04-08 09:57:32 +02:00
|
|
|
|
byteq_fast_forward(&rpc->input);
|
2009-10-26 15:04:05 -07:00
|
|
|
|
chunk = byteq_headroom(&rpc->input);
|
|
|
|
|
retval = stream_recv(rpc->stream, byteq_head(&rpc->input), chunk);
|
|
|
|
|
if (retval < 0) {
|
|
|
|
|
if (retval == -EAGAIN) {
|
|
|
|
|
return EAGAIN;
|
|
|
|
|
} else {
|
|
|
|
|
VLOG_WARN_RL(&rl, "%s: receive error: %s",
|
2013-06-24 10:54:49 -07:00
|
|
|
|
rpc->name, ovs_strerror(-retval));
|
2009-10-26 15:04:05 -07:00
|
|
|
|
jsonrpc_error(rpc, -retval);
|
|
|
|
|
return rpc->status;
|
|
|
|
|
}
|
|
|
|
|
} else if (retval == 0) {
|
|
|
|
|
jsonrpc_error(rpc, EOF);
|
|
|
|
|
return EOF;
|
|
|
|
|
}
|
|
|
|
|
byteq_advance_head(&rpc->input, retval);
|
2014-04-03 15:27:18 -07:00
|
|
|
|
}
|
2009-10-26 15:04:05 -07:00
|
|
|
|
|
2014-04-03 15:27:18 -07:00
|
|
|
|
/* We have some input. Feed it into the JSON parser. */
|
|
|
|
|
if (!rpc->parser) {
|
|
|
|
|
rpc->parser = json_parser_create(0);
|
|
|
|
|
}
|
|
|
|
|
n = byteq_tailroom(&rpc->input);
|
|
|
|
|
used = json_parser_feed(rpc->parser,
|
|
|
|
|
(char *) byteq_tail(&rpc->input), n);
|
|
|
|
|
byteq_advance_tail(&rpc->input, used);
|
|
|
|
|
|
|
|
|
|
/* If we have complete JSON, attempt to parse it as JSON-RPC. */
|
|
|
|
|
if (json_parser_is_done(rpc->parser)) {
|
|
|
|
|
*msgp = jsonrpc_parse_received_message(rpc);
|
|
|
|
|
if (*msgp) {
|
|
|
|
|
return 0;
|
2009-10-26 15:04:05 -07:00
|
|
|
|
}
|
2014-04-03 15:27:18 -07:00
|
|
|
|
|
|
|
|
|
if (rpc->status) {
|
|
|
|
|
const struct byteq *q = &rpc->input;
|
|
|
|
|
if (q->head <= q->size) {
|
|
|
|
|
stream_report_content(q->buffer, q->head, STREAM_JSONRPC,
|
2016-02-03 13:21:10 -08:00
|
|
|
|
&this_module, rpc->name);
|
2009-10-26 15:04:05 -07:00
|
|
|
|
}
|
2014-04-03 15:27:18 -07:00
|
|
|
|
return rpc->status;
|
2009-10-26 15:04:05 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-08 09:57:31 +02:00
|
|
|
|
/* We tried hard but didn't get a complete JSON message within the above
|
|
|
|
|
* iterations. We want to know how often we abort for this reason. */
|
|
|
|
|
COVERAGE_INC(jsonrpc_recv_incomplete);
|
|
|
|
|
|
2012-04-24 10:57:41 -07:00
|
|
|
|
return EAGAIN;
|
2009-10-26 15:04:05 -07:00
|
|
|
|
}
|
|
|
|
|
|
2012-02-27 09:57:36 -08:00
|
|
|
|
/* Causes the poll loop to wake up when jsonrpc_recv() may return a value other
|
|
|
|
|
* than EAGAIN. */
|
2009-10-26 15:04:05 -07:00
|
|
|
|
void
|
|
|
|
|
jsonrpc_recv_wait(struct jsonrpc *rpc)
|
|
|
|
|
{
|
2014-04-03 15:27:18 -07:00
|
|
|
|
if (rpc->status || !byteq_is_empty(&rpc->input)) {
|
2013-07-29 15:24:45 -07:00
|
|
|
|
poll_immediate_wake_at(rpc->name);
|
2009-10-26 15:04:05 -07:00
|
|
|
|
} else {
|
|
|
|
|
stream_recv_wait(rpc->stream);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-27 09:57:36 -08:00
|
|
|
|
/* Sends 'msg' on 'rpc' and waits for it to be successfully queued to the
|
|
|
|
|
* underlying stream. Returns 0 if 'msg' was sent successfully, otherwise a
|
|
|
|
|
* status value (see jsonrpc_get_status()).
|
|
|
|
|
*
|
|
|
|
|
* Always takes ownership of 'msg', regardless of success. */
|
2009-10-26 15:04:05 -07:00
|
|
|
|
int
|
|
|
|
|
jsonrpc_send_block(struct jsonrpc *rpc, struct jsonrpc_msg *msg)
|
|
|
|
|
{
|
|
|
|
|
int error;
|
|
|
|
|
|
2010-04-13 09:28:13 -07:00
|
|
|
|
fatal_signal_run();
|
|
|
|
|
|
2009-10-26 15:04:05 -07:00
|
|
|
|
error = jsonrpc_send(rpc, msg);
|
|
|
|
|
if (error) {
|
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-11 13:06:42 -08:00
|
|
|
|
for (;;) {
|
2009-10-26 15:04:05 -07:00
|
|
|
|
jsonrpc_run(rpc);
|
2016-03-25 14:10:22 -07:00
|
|
|
|
if (ovs_list_is_empty(&rpc->output) || rpc->status) {
|
2010-01-11 13:06:42 -08:00
|
|
|
|
return rpc->status;
|
|
|
|
|
}
|
2009-10-26 15:04:05 -07:00
|
|
|
|
jsonrpc_wait(rpc);
|
|
|
|
|
poll_block();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-27 09:57:36 -08:00
|
|
|
|
/* Waits for a message to be received on 'rpc'. Same semantics as
|
|
|
|
|
* jsonrpc_recv() except that EAGAIN will never be returned. */
|
2009-10-26 15:04:05 -07:00
|
|
|
|
int
|
|
|
|
|
jsonrpc_recv_block(struct jsonrpc *rpc, struct jsonrpc_msg **msgp)
|
|
|
|
|
{
|
|
|
|
|
for (;;) {
|
|
|
|
|
int error = jsonrpc_recv(rpc, msgp);
|
|
|
|
|
if (error != EAGAIN) {
|
2010-04-13 09:28:13 -07:00
|
|
|
|
fatal_signal_run();
|
2009-10-26 15:04:05 -07:00
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
jsonrpc_run(rpc);
|
|
|
|
|
jsonrpc_wait(rpc);
|
|
|
|
|
jsonrpc_recv_wait(rpc);
|
|
|
|
|
poll_block();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-27 09:57:36 -08:00
|
|
|
|
/* Sends 'request' to 'rpc' then waits for a reply. The return value is 0 if
|
|
|
|
|
* successful, in which case '*replyp' is set to the reply, which the caller
|
|
|
|
|
* must eventually free with jsonrpc_msg_destroy(). Otherwise returns a status
|
|
|
|
|
* value (see jsonrpc_get_status()).
|
|
|
|
|
*
|
|
|
|
|
* Discards any message received on 'rpc' that is not a reply to 'request'
|
|
|
|
|
* (based on message id).
|
|
|
|
|
*
|
|
|
|
|
* Always takes ownership of 'request', regardless of success. */
|
2009-11-06 15:35:34 -08:00
|
|
|
|
int
|
|
|
|
|
jsonrpc_transact_block(struct jsonrpc *rpc, struct jsonrpc_msg *request,
|
|
|
|
|
struct jsonrpc_msg **replyp)
|
|
|
|
|
{
|
|
|
|
|
struct jsonrpc_msg *reply = NULL;
|
|
|
|
|
struct json *id;
|
|
|
|
|
int error;
|
|
|
|
|
|
|
|
|
|
id = json_clone(request->id);
|
|
|
|
|
error = jsonrpc_send_block(rpc, request);
|
|
|
|
|
if (!error) {
|
|
|
|
|
for (;;) {
|
|
|
|
|
error = jsonrpc_recv_block(rpc, &reply);
|
2012-02-15 19:38:27 -08:00
|
|
|
|
if (error) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if ((reply->type == JSONRPC_REPLY || reply->type == JSONRPC_ERROR)
|
|
|
|
|
&& json_equal(id, reply->id)) {
|
2009-11-06 15:35:34 -08:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
jsonrpc_msg_destroy(reply);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
*replyp = error ? NULL : reply;
|
|
|
|
|
json_destroy(id);
|
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-03 15:27:18 -07:00
|
|
|
|
/* Attempts to parse the content of 'rpc->parser' (which is complete JSON) as a
|
|
|
|
|
* JSON-RPC message. If successful, returns the JSON-RPC message. On failure,
|
|
|
|
|
* signals an error on 'rpc' with jsonrpc_error() and returns NULL. */
|
|
|
|
|
static struct jsonrpc_msg *
|
|
|
|
|
jsonrpc_parse_received_message(struct jsonrpc *rpc)
|
2009-10-26 15:04:05 -07:00
|
|
|
|
{
|
|
|
|
|
struct jsonrpc_msg *msg;
|
|
|
|
|
struct json *json;
|
|
|
|
|
char *error;
|
|
|
|
|
|
|
|
|
|
json = json_parser_finish(rpc->parser);
|
|
|
|
|
rpc->parser = NULL;
|
|
|
|
|
if (json->type == JSON_STRING) {
|
|
|
|
|
VLOG_WARN_RL(&rl, "%s: error parsing stream: %s",
|
|
|
|
|
rpc->name, json_string(json));
|
|
|
|
|
jsonrpc_error(rpc, EPROTO);
|
|
|
|
|
json_destroy(json);
|
2014-04-03 15:27:18 -07:00
|
|
|
|
return NULL;
|
2009-10-26 15:04:05 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
error = jsonrpc_msg_from_json(json, &msg);
|
|
|
|
|
if (error) {
|
|
|
|
|
VLOG_WARN_RL(&rl, "%s: received bad JSON-RPC message: %s",
|
|
|
|
|
rpc->name, error);
|
|
|
|
|
free(error);
|
|
|
|
|
jsonrpc_error(rpc, EPROTO);
|
2014-04-03 15:27:18 -07:00
|
|
|
|
return NULL;
|
2009-10-26 15:04:05 -07:00
|
|
|
|
}
|
|
|
|
|
|
2009-11-16 16:55:35 -08:00
|
|
|
|
jsonrpc_log_msg(rpc, "received", msg);
|
2014-04-03 15:27:18 -07:00
|
|
|
|
return msg;
|
2009-10-26 15:04:05 -07:00
|
|
|
|
}
|
|
|
|
|
|
2012-02-17 16:47:36 -08:00
|
|
|
|
static void
|
2009-10-26 15:04:05 -07:00
|
|
|
|
jsonrpc_error(struct jsonrpc *rpc, int error)
|
|
|
|
|
{
|
2012-11-06 13:14:55 -08:00
|
|
|
|
ovs_assert(error);
|
2009-10-26 15:04:05 -07:00
|
|
|
|
if (!rpc->status) {
|
|
|
|
|
rpc->status = error;
|
|
|
|
|
jsonrpc_cleanup(rpc);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
jsonrpc_cleanup(struct jsonrpc *rpc)
|
|
|
|
|
{
|
|
|
|
|
stream_close(rpc->stream);
|
|
|
|
|
rpc->stream = NULL;
|
|
|
|
|
|
|
|
|
|
json_parser_abort(rpc->parser);
|
|
|
|
|
rpc->parser = NULL;
|
|
|
|
|
|
2010-12-06 10:03:31 -08:00
|
|
|
|
ofpbuf_list_delete(&rpc->output);
|
2009-10-26 15:04:05 -07:00
|
|
|
|
rpc->backlog = 0;
|
2014-09-18 10:49:47 -07:00
|
|
|
|
rpc->output_count = 0;
|
2009-10-26 15:04:05 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct jsonrpc_msg *
|
|
|
|
|
jsonrpc_create(enum jsonrpc_msg_type type, const char *method,
|
|
|
|
|
struct json *params, struct json *result, struct json *error,
|
|
|
|
|
struct json *id)
|
|
|
|
|
{
|
|
|
|
|
struct jsonrpc_msg *msg = xmalloc(sizeof *msg);
|
|
|
|
|
msg->type = type;
|
2016-06-24 21:23:16 -07:00
|
|
|
|
msg->method = nullable_xstrdup(method);
|
2009-10-26 15:04:05 -07:00
|
|
|
|
msg->params = params;
|
|
|
|
|
msg->result = result;
|
|
|
|
|
msg->error = error;
|
|
|
|
|
msg->id = id;
|
|
|
|
|
return msg;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct json *
|
|
|
|
|
jsonrpc_create_id(void)
|
|
|
|
|
{
|
2014-08-29 10:34:52 -07:00
|
|
|
|
static atomic_count next_id = ATOMIC_COUNT_INIT(0);
|
2013-04-15 14:35:18 -07:00
|
|
|
|
unsigned int id;
|
|
|
|
|
|
2014-08-29 10:34:52 -07:00
|
|
|
|
id = atomic_count_inc(&next_id);
|
2013-04-15 14:35:18 -07:00
|
|
|
|
return json_integer_create(id);
|
2009-10-26 15:04:05 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct jsonrpc_msg *
|
2009-12-01 16:32:03 -08:00
|
|
|
|
jsonrpc_create_request(const char *method, struct json *params,
|
|
|
|
|
struct json **idp)
|
2009-10-26 15:04:05 -07:00
|
|
|
|
{
|
2009-12-01 16:32:03 -08:00
|
|
|
|
struct json *id = jsonrpc_create_id();
|
|
|
|
|
if (idp) {
|
|
|
|
|
*idp = json_clone(id);
|
|
|
|
|
}
|
|
|
|
|
return jsonrpc_create(JSONRPC_REQUEST, method, params, NULL, NULL, id);
|
2009-10-26 15:04:05 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct jsonrpc_msg *
|
|
|
|
|
jsonrpc_create_notify(const char *method, struct json *params)
|
|
|
|
|
{
|
|
|
|
|
return jsonrpc_create(JSONRPC_NOTIFY, method, params, NULL, NULL, NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct jsonrpc_msg *
|
|
|
|
|
jsonrpc_create_reply(struct json *result, const struct json *id)
|
|
|
|
|
{
|
|
|
|
|
return jsonrpc_create(JSONRPC_REPLY, NULL, NULL, result, NULL,
|
|
|
|
|
json_clone(id));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct jsonrpc_msg *
|
|
|
|
|
jsonrpc_create_error(struct json *error, const struct json *id)
|
|
|
|
|
{
|
|
|
|
|
return jsonrpc_create(JSONRPC_REPLY, NULL, NULL, NULL, error,
|
|
|
|
|
json_clone(id));
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-31 21:15:58 -08:00
|
|
|
|
struct jsonrpc_msg *
|
|
|
|
|
jsonrpc_msg_clone(const struct jsonrpc_msg *old)
|
|
|
|
|
{
|
|
|
|
|
return jsonrpc_create(old->type, old->method,
|
|
|
|
|
json_nullable_clone(old->params),
|
|
|
|
|
json_nullable_clone(old->result),
|
|
|
|
|
json_nullable_clone(old->error),
|
|
|
|
|
json_nullable_clone(old->id));
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-26 15:04:05 -07:00
|
|
|
|
const char *
|
|
|
|
|
jsonrpc_msg_type_to_string(enum jsonrpc_msg_type type)
|
|
|
|
|
{
|
|
|
|
|
switch (type) {
|
|
|
|
|
case JSONRPC_REQUEST:
|
|
|
|
|
return "request";
|
|
|
|
|
|
|
|
|
|
case JSONRPC_NOTIFY:
|
|
|
|
|
return "notification";
|
|
|
|
|
|
|
|
|
|
case JSONRPC_REPLY:
|
|
|
|
|
return "reply";
|
|
|
|
|
|
|
|
|
|
case JSONRPC_ERROR:
|
|
|
|
|
return "error";
|
|
|
|
|
}
|
|
|
|
|
return "(null)";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char *
|
|
|
|
|
jsonrpc_msg_is_valid(const struct jsonrpc_msg *m)
|
|
|
|
|
{
|
|
|
|
|
const char *type_name;
|
|
|
|
|
unsigned int pattern;
|
|
|
|
|
|
|
|
|
|
if (m->params && m->params->type != JSON_ARRAY) {
|
|
|
|
|
return xstrdup("\"params\" must be JSON array");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (m->type) {
|
|
|
|
|
case JSONRPC_REQUEST:
|
|
|
|
|
pattern = 0x11001;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case JSONRPC_NOTIFY:
|
|
|
|
|
pattern = 0x11000;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case JSONRPC_REPLY:
|
|
|
|
|
pattern = 0x00101;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case JSONRPC_ERROR:
|
|
|
|
|
pattern = 0x00011;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
return xasprintf("invalid JSON-RPC message type %d", m->type);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type_name = jsonrpc_msg_type_to_string(m->type);
|
|
|
|
|
if ((m->method != NULL) != ((pattern & 0x10000) != 0)) {
|
|
|
|
|
return xasprintf("%s must%s have \"method\"",
|
|
|
|
|
type_name, (pattern & 0x10000) ? "" : " not");
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
if ((m->params != NULL) != ((pattern & 0x1000) != 0)) {
|
|
|
|
|
return xasprintf("%s must%s have \"params\"",
|
|
|
|
|
type_name, (pattern & 0x1000) ? "" : " not");
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
if ((m->result != NULL) != ((pattern & 0x100) != 0)) {
|
|
|
|
|
return xasprintf("%s must%s have \"result\"",
|
|
|
|
|
type_name, (pattern & 0x100) ? "" : " not");
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
if ((m->error != NULL) != ((pattern & 0x10) != 0)) {
|
|
|
|
|
return xasprintf("%s must%s have \"error\"",
|
|
|
|
|
type_name, (pattern & 0x10) ? "" : " not");
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
if ((m->id != NULL) != ((pattern & 0x1) != 0)) {
|
|
|
|
|
return xasprintf("%s must%s have \"id\"",
|
|
|
|
|
type_name, (pattern & 0x1) ? "" : " not");
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
jsonrpc_msg_destroy(struct jsonrpc_msg *m)
|
|
|
|
|
{
|
|
|
|
|
if (m) {
|
|
|
|
|
free(m->method);
|
|
|
|
|
json_destroy(m->params);
|
|
|
|
|
json_destroy(m->result);
|
|
|
|
|
json_destroy(m->error);
|
|
|
|
|
json_destroy(m->id);
|
|
|
|
|
free(m);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct json *
|
|
|
|
|
null_from_json_null(struct json *json)
|
|
|
|
|
{
|
|
|
|
|
if (json && json->type == JSON_NULL) {
|
|
|
|
|
json_destroy(json);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
return json;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char *
|
|
|
|
|
jsonrpc_msg_from_json(struct json *json, struct jsonrpc_msg **msgp)
|
|
|
|
|
{
|
|
|
|
|
struct json *method = NULL;
|
|
|
|
|
struct jsonrpc_msg *msg = NULL;
|
|
|
|
|
struct shash *object;
|
|
|
|
|
char *error;
|
|
|
|
|
|
|
|
|
|
if (json->type != JSON_OBJECT) {
|
|
|
|
|
error = xstrdup("message is not a JSON object");
|
|
|
|
|
goto exit;
|
|
|
|
|
}
|
|
|
|
|
object = json_object(json);
|
|
|
|
|
|
|
|
|
|
method = shash_find_and_delete(object, "method");
|
|
|
|
|
if (method && method->type != JSON_STRING) {
|
|
|
|
|
error = xstrdup("method is not a JSON string");
|
|
|
|
|
goto exit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
msg = xzalloc(sizeof *msg);
|
2025-06-24 21:54:31 +02:00
|
|
|
|
msg->method = method ? xstrdup(json_string(method)) : NULL;
|
2009-10-26 15:04:05 -07:00
|
|
|
|
msg->params = null_from_json_null(shash_find_and_delete(object, "params"));
|
|
|
|
|
msg->result = null_from_json_null(shash_find_and_delete(object, "result"));
|
|
|
|
|
msg->error = null_from_json_null(shash_find_and_delete(object, "error"));
|
|
|
|
|
msg->id = null_from_json_null(shash_find_and_delete(object, "id"));
|
|
|
|
|
msg->type = (msg->result ? JSONRPC_REPLY
|
|
|
|
|
: msg->error ? JSONRPC_ERROR
|
|
|
|
|
: msg->id ? JSONRPC_REQUEST
|
|
|
|
|
: JSONRPC_NOTIFY);
|
|
|
|
|
if (!shash_is_empty(object)) {
|
|
|
|
|
error = xasprintf("message has unexpected member \"%s\"",
|
|
|
|
|
shash_first(object)->name);
|
|
|
|
|
goto exit;
|
|
|
|
|
}
|
|
|
|
|
error = jsonrpc_msg_is_valid(msg);
|
|
|
|
|
if (error) {
|
|
|
|
|
goto exit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
exit:
|
|
|
|
|
json_destroy(method);
|
|
|
|
|
json_destroy(json);
|
|
|
|
|
if (error) {
|
|
|
|
|
jsonrpc_msg_destroy(msg);
|
|
|
|
|
msg = NULL;
|
|
|
|
|
}
|
|
|
|
|
*msgp = msg;
|
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-29 05:44:35 -08:00
|
|
|
|
/* Returns 'm' converted to JSON suitable for sending as a JSON-RPC message.
|
|
|
|
|
*
|
|
|
|
|
* Consumes and destroys 'm'. */
|
2009-10-26 15:04:05 -07:00
|
|
|
|
struct json *
|
|
|
|
|
jsonrpc_msg_to_json(struct jsonrpc_msg *m)
|
|
|
|
|
{
|
|
|
|
|
struct json *json = json_object_create();
|
|
|
|
|
|
|
|
|
|
if (m->method) {
|
|
|
|
|
json_object_put(json, "method", json_string_create_nocopy(m->method));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m->params) {
|
|
|
|
|
json_object_put(json, "params", m->params);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m->result) {
|
|
|
|
|
json_object_put(json, "result", m->result);
|
|
|
|
|
} else if (m->type == JSONRPC_ERROR) {
|
|
|
|
|
json_object_put(json, "result", json_null_create());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m->error) {
|
|
|
|
|
json_object_put(json, "error", m->error);
|
|
|
|
|
} else if (m->type == JSONRPC_REPLY) {
|
|
|
|
|
json_object_put(json, "error", json_null_create());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m->id) {
|
|
|
|
|
json_object_put(json, "id", m->id);
|
|
|
|
|
} else if (m->type == JSONRPC_NOTIFY) {
|
|
|
|
|
json_object_put(json, "id", json_null_create());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free(m);
|
|
|
|
|
|
|
|
|
|
return json;
|
|
|
|
|
}
|
2017-12-31 21:15:58 -08:00
|
|
|
|
|
|
|
|
|
char *
|
|
|
|
|
jsonrpc_msg_to_string(const struct jsonrpc_msg *m)
|
|
|
|
|
{
|
|
|
|
|
struct jsonrpc_msg *copy = jsonrpc_msg_clone(m);
|
|
|
|
|
struct json *json = jsonrpc_msg_to_json(copy);
|
|
|
|
|
char *s = json_to_string(json, JSSF_SORT);
|
|
|
|
|
json_destroy(json);
|
|
|
|
|
return s;
|
|
|
|
|
}
|
2009-12-02 10:50:18 -08:00
|
|
|
|
|
|
|
|
|
/* A JSON-RPC session with reconnection. */
|
|
|
|
|
|
|
|
|
|
struct jsonrpc_session {
|
2018-01-22 11:09:40 -08:00
|
|
|
|
struct svec remotes;
|
|
|
|
|
size_t next_remote;
|
|
|
|
|
|
2009-12-02 10:50:18 -08:00
|
|
|
|
struct reconnect *reconnect;
|
|
|
|
|
struct jsonrpc *rpc;
|
|
|
|
|
struct stream *stream;
|
2010-03-24 10:14:39 -07:00
|
|
|
|
struct pstream *pstream;
|
2013-03-15 16:14:28 -07:00
|
|
|
|
int last_error;
|
2009-12-02 10:50:18 -08:00
|
|
|
|
unsigned int seqno;
|
2012-04-11 19:52:46 -07:00
|
|
|
|
uint8_t dscp;
|
2020-10-21 03:32:49 +02:00
|
|
|
|
|
|
|
|
|
/* Limits for jsonrpc. */
|
|
|
|
|
size_t max_n_msgs;
|
|
|
|
|
size_t max_backlog_bytes;
|
2009-12-02 10:50:18 -08:00
|
|
|
|
};
|
|
|
|
|
|
2018-01-22 11:09:40 -08:00
|
|
|
|
static void
|
|
|
|
|
jsonrpc_session_pick_remote(struct jsonrpc_session *s)
|
|
|
|
|
{
|
|
|
|
|
reconnect_set_name(s->reconnect,
|
|
|
|
|
s->remotes.names[s->next_remote++ % s->remotes.n]);
|
|
|
|
|
}
|
|
|
|
|
|
2010-03-24 10:14:39 -07:00
|
|
|
|
/* Creates and returns a jsonrpc_session to 'name', which should be a string
|
|
|
|
|
* acceptable to stream_open() or pstream_open().
|
|
|
|
|
*
|
|
|
|
|
* If 'name' is an active connection method, e.g. "tcp:127.1.2.3", the new
|
2013-03-15 16:14:28 -07:00
|
|
|
|
* jsonrpc_session connects to 'name'. If 'retry' is true, then the new
|
|
|
|
|
* session connects and reconnects to 'name', with backoff. If 'retry' is
|
|
|
|
|
* false, the new session will only try to connect once and after a connection
|
|
|
|
|
* failure or a disconnection jsonrpc_session_is_alive() will return false for
|
|
|
|
|
* the new session.
|
2010-03-24 10:14:39 -07:00
|
|
|
|
*
|
|
|
|
|
* If 'name' is a passive connection method, e.g. "ptcp:", the new
|
|
|
|
|
* jsonrpc_session listens for connections to 'name'. It maintains at most one
|
|
|
|
|
* connection at any given time. Any new connection causes the previous one
|
|
|
|
|
* (if any) to be dropped. */
|
2009-12-02 10:50:18 -08:00
|
|
|
|
struct jsonrpc_session *
|
2013-03-15 16:14:28 -07:00
|
|
|
|
jsonrpc_session_open(const char *name, bool retry)
|
2018-01-22 11:09:40 -08:00
|
|
|
|
{
|
|
|
|
|
const struct svec remotes = { .names = (char **) &name, .n = 1 };
|
|
|
|
|
return jsonrpc_session_open_multiple(&remotes, retry);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct jsonrpc_session *
|
|
|
|
|
jsonrpc_session_open_multiple(const struct svec *remotes, bool retry)
|
2009-12-02 10:50:18 -08:00
|
|
|
|
{
|
|
|
|
|
struct jsonrpc_session *s;
|
|
|
|
|
|
|
|
|
|
s = xmalloc(sizeof *s);
|
2018-01-22 11:09:40 -08:00
|
|
|
|
|
2019-04-12 16:26:24 -07:00
|
|
|
|
/* Set 'n' remotes from 'names'. */
|
2018-01-22 11:09:40 -08:00
|
|
|
|
svec_clone(&s->remotes, remotes);
|
2020-06-26 12:46:10 -07:00
|
|
|
|
if (!s->remotes.n) {
|
|
|
|
|
svec_add(&s->remotes, "invalid:");
|
|
|
|
|
}
|
2018-01-22 11:09:40 -08:00
|
|
|
|
s->next_remote = 0;
|
|
|
|
|
|
2009-12-02 10:50:18 -08:00
|
|
|
|
s->reconnect = reconnect_create(time_msec());
|
2018-01-22 11:09:40 -08:00
|
|
|
|
jsonrpc_session_pick_remote(s);
|
2009-12-02 10:50:18 -08:00
|
|
|
|
reconnect_enable(s->reconnect, time_msec());
|
2018-01-22 11:09:40 -08:00
|
|
|
|
reconnect_set_backoff_free_tries(s->reconnect, remotes->n);
|
2009-12-02 10:50:18 -08:00
|
|
|
|
s->rpc = NULL;
|
|
|
|
|
s->stream = NULL;
|
2010-03-24 10:14:39 -07:00
|
|
|
|
s->pstream = NULL;
|
2009-12-02 10:50:18 -08:00
|
|
|
|
s->seqno = 0;
|
2012-04-11 19:52:46 -07:00
|
|
|
|
s->dscp = 0;
|
2013-03-15 16:14:28 -07:00
|
|
|
|
s->last_error = 0;
|
2009-12-02 10:50:18 -08:00
|
|
|
|
|
2020-10-21 03:32:49 +02:00
|
|
|
|
jsonrpc_session_set_backlog_threshold(s, 0, 0);
|
|
|
|
|
|
2018-01-22 11:09:40 -08:00
|
|
|
|
const char *name = reconnect_get_name(s->reconnect);
|
2010-03-24 10:14:39 -07:00
|
|
|
|
if (!pstream_verify_name(name)) {
|
|
|
|
|
reconnect_set_passive(s->reconnect, true, time_msec());
|
2013-03-15 16:14:28 -07:00
|
|
|
|
} else if (!retry) {
|
2018-01-22 11:09:40 -08:00
|
|
|
|
reconnect_set_max_tries(s->reconnect, remotes->n);
|
2013-03-15 16:14:28 -07:00
|
|
|
|
reconnect_set_backoff(s->reconnect, INT_MAX, INT_MAX);
|
2010-03-24 10:14:39 -07:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-27 15:29:04 +02:00
|
|
|
|
if (!stream_or_pstream_needs_probes(name) || ovs_replay_is_active()) {
|
2012-04-11 20:18:34 -07:00
|
|
|
|
reconnect_set_probe_interval(s->reconnect, 0);
|
|
|
|
|
}
|
|
|
|
|
|
2009-12-02 10:50:18 -08:00
|
|
|
|
return s;
|
|
|
|
|
}
|
|
|
|
|
|
2009-12-17 15:16:43 -08:00
|
|
|
|
/* Creates and returns a jsonrpc_session that is initially connected to
|
2010-06-11 14:45:24 -07:00
|
|
|
|
* 'jsonrpc'. If the connection is dropped, it will not be reconnected.
|
|
|
|
|
*
|
|
|
|
|
* On the assumption that such connections are likely to be short-lived
|
|
|
|
|
* (e.g. from ovs-vsctl), informational logging for them is suppressed. */
|
2009-12-17 15:16:43 -08:00
|
|
|
|
struct jsonrpc_session *
|
2012-10-04 12:33:05 -07:00
|
|
|
|
jsonrpc_session_open_unreliably(struct jsonrpc *jsonrpc, uint8_t dscp)
|
2009-12-17 15:16:43 -08:00
|
|
|
|
{
|
|
|
|
|
struct jsonrpc_session *s;
|
|
|
|
|
|
|
|
|
|
s = xmalloc(sizeof *s);
|
2018-01-22 11:09:40 -08:00
|
|
|
|
svec_init(&s->remotes);
|
|
|
|
|
svec_add(&s->remotes, jsonrpc_get_name(jsonrpc));
|
|
|
|
|
s->next_remote = 0;
|
2009-12-17 15:16:43 -08:00
|
|
|
|
s->reconnect = reconnect_create(time_msec());
|
2010-06-11 14:45:24 -07:00
|
|
|
|
reconnect_set_quiet(s->reconnect, true);
|
2009-12-17 15:16:43 -08:00
|
|
|
|
reconnect_set_name(s->reconnect, jsonrpc_get_name(jsonrpc));
|
|
|
|
|
reconnect_set_max_tries(s->reconnect, 0);
|
|
|
|
|
reconnect_connected(s->reconnect, time_msec());
|
2021-05-27 15:29:04 +02:00
|
|
|
|
|
|
|
|
|
if (ovs_replay_is_active()) {
|
|
|
|
|
reconnect_set_probe_interval(s->reconnect, 0);
|
|
|
|
|
}
|
|
|
|
|
|
2012-10-04 12:33:05 -07:00
|
|
|
|
s->dscp = dscp;
|
2009-12-17 15:16:43 -08:00
|
|
|
|
s->rpc = jsonrpc;
|
|
|
|
|
s->stream = NULL;
|
2010-03-24 10:14:39 -07:00
|
|
|
|
s->pstream = NULL;
|
2017-10-04 23:38:43 -07:00
|
|
|
|
s->seqno = 1;
|
2009-12-17 15:16:43 -08:00
|
|
|
|
|
2020-10-21 03:32:49 +02:00
|
|
|
|
jsonrpc_session_set_backlog_threshold(s, 0, 0);
|
2009-12-17 15:16:43 -08:00
|
|
|
|
return s;
|
|
|
|
|
}
|
|
|
|
|
|
2009-12-02 10:50:18 -08:00
|
|
|
|
void
|
|
|
|
|
jsonrpc_session_close(struct jsonrpc_session *s)
|
|
|
|
|
{
|
|
|
|
|
if (s) {
|
|
|
|
|
jsonrpc_close(s->rpc);
|
|
|
|
|
reconnect_destroy(s->reconnect);
|
2010-03-23 17:20:42 -07:00
|
|
|
|
stream_close(s->stream);
|
2010-03-24 10:14:39 -07:00
|
|
|
|
pstream_close(s->pstream);
|
2018-01-22 11:09:40 -08:00
|
|
|
|
svec_destroy(&s->remotes);
|
2009-12-02 10:50:18 -08:00
|
|
|
|
free(s);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-31 21:15:58 -08:00
|
|
|
|
struct jsonrpc *
|
|
|
|
|
jsonrpc_session_steal(struct jsonrpc_session *s)
|
|
|
|
|
{
|
|
|
|
|
struct jsonrpc *rpc = s->rpc;
|
|
|
|
|
s->rpc = NULL;
|
|
|
|
|
jsonrpc_session_close(s);
|
|
|
|
|
return rpc;
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-29 12:56:18 +02:00
|
|
|
|
void
|
|
|
|
|
jsonrpc_session_replace(struct jsonrpc_session *s, struct jsonrpc *rpc)
|
|
|
|
|
{
|
|
|
|
|
if (s->rpc) {
|
|
|
|
|
jsonrpc_close(s->rpc);
|
|
|
|
|
}
|
|
|
|
|
s->rpc = rpc;
|
|
|
|
|
if (s->rpc) {
|
|
|
|
|
reconnect_set_name(s->reconnect, jsonrpc_get_name(s->rpc));
|
|
|
|
|
reconnect_connected(s->reconnect, time_msec());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-12-02 10:50:18 -08:00
|
|
|
|
static void
|
|
|
|
|
jsonrpc_session_disconnect(struct jsonrpc_session *s)
|
|
|
|
|
{
|
|
|
|
|
if (s->rpc) {
|
|
|
|
|
jsonrpc_error(s->rpc, EOF);
|
|
|
|
|
jsonrpc_close(s->rpc);
|
|
|
|
|
s->rpc = NULL;
|
|
|
|
|
} else if (s->stream) {
|
|
|
|
|
stream_close(s->stream);
|
|
|
|
|
s->stream = NULL;
|
2018-01-22 11:09:40 -08:00
|
|
|
|
} else {
|
|
|
|
|
return;
|
2009-12-02 10:50:18 -08:00
|
|
|
|
}
|
2018-01-22 11:09:40 -08:00
|
|
|
|
|
|
|
|
|
s->seqno++;
|
|
|
|
|
jsonrpc_session_pick_remote(s);
|
2009-12-02 10:50:18 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
jsonrpc_session_connect(struct jsonrpc_session *s)
|
|
|
|
|
{
|
2010-03-24 10:14:39 -07:00
|
|
|
|
const char *name = reconnect_get_name(s->reconnect);
|
2009-12-02 10:50:18 -08:00
|
|
|
|
int error;
|
|
|
|
|
|
|
|
|
|
jsonrpc_session_disconnect(s);
|
2010-03-24 10:14:39 -07:00
|
|
|
|
if (!reconnect_is_passive(s->reconnect)) {
|
2012-04-11 19:52:46 -07:00
|
|
|
|
error = jsonrpc_stream_open(name, &s->stream, s->dscp);
|
2010-03-24 10:14:39 -07:00
|
|
|
|
if (!error) {
|
|
|
|
|
reconnect_connecting(s->reconnect, time_msec());
|
2013-03-15 16:14:28 -07:00
|
|
|
|
} else {
|
|
|
|
|
s->last_error = error;
|
2010-03-24 10:14:39 -07:00
|
|
|
|
}
|
|
|
|
|
} else {
|
2012-03-10 15:58:10 -08:00
|
|
|
|
error = s->pstream ? 0 : jsonrpc_pstream_open(name, &s->pstream,
|
2012-04-11 19:52:46 -07:00
|
|
|
|
s->dscp);
|
2010-03-24 10:14:39 -07:00
|
|
|
|
if (!error) {
|
|
|
|
|
reconnect_listening(s->reconnect, time_msec());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-12-02 10:50:18 -08:00
|
|
|
|
if (error) {
|
|
|
|
|
reconnect_connect_failed(s->reconnect, time_msec(), error);
|
2018-01-22 11:09:40 -08:00
|
|
|
|
jsonrpc_session_pick_remote(s);
|
2009-12-02 10:50:18 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
jsonrpc_session_run(struct jsonrpc_session *s)
|
|
|
|
|
{
|
2010-03-24 10:14:39 -07:00
|
|
|
|
if (s->pstream) {
|
|
|
|
|
struct stream *stream;
|
|
|
|
|
int error;
|
|
|
|
|
|
|
|
|
|
error = pstream_accept(s->pstream, &stream);
|
|
|
|
|
if (!error) {
|
|
|
|
|
if (s->rpc || s->stream) {
|
|
|
|
|
VLOG_INFO_RL(&rl,
|
|
|
|
|
"%s: new connection replacing active connection",
|
|
|
|
|
reconnect_get_name(s->reconnect));
|
|
|
|
|
jsonrpc_session_disconnect(s);
|
|
|
|
|
}
|
|
|
|
|
reconnect_connected(s->reconnect, time_msec());
|
|
|
|
|
s->rpc = jsonrpc_open(stream);
|
2020-10-21 03:32:49 +02:00
|
|
|
|
jsonrpc_set_backlog_threshold(s->rpc, s->max_n_msgs,
|
|
|
|
|
s->max_backlog_bytes);
|
2017-10-04 23:38:43 -07:00
|
|
|
|
s->seqno++;
|
2010-03-24 10:14:39 -07:00
|
|
|
|
} else if (error != EAGAIN) {
|
|
|
|
|
reconnect_listen_error(s->reconnect, time_msec(), error);
|
|
|
|
|
pstream_close(s->pstream);
|
|
|
|
|
s->pstream = NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-12-02 10:50:18 -08:00
|
|
|
|
if (s->rpc) {
|
2012-09-07 10:50:15 -07:00
|
|
|
|
size_t backlog;
|
2009-12-02 10:50:18 -08:00
|
|
|
|
int error;
|
|
|
|
|
|
2012-09-07 10:50:15 -07:00
|
|
|
|
backlog = jsonrpc_get_backlog(s->rpc);
|
2009-12-02 10:50:18 -08:00
|
|
|
|
jsonrpc_run(s->rpc);
|
2012-09-07 10:50:15 -07:00
|
|
|
|
if (jsonrpc_get_backlog(s->rpc) < backlog) {
|
|
|
|
|
/* Data previously caught in a queue was successfully sent (or
|
|
|
|
|
* there's an error, which we'll catch below.)
|
|
|
|
|
*
|
|
|
|
|
* We don't count data that is successfully sent immediately as
|
|
|
|
|
* activity, because there's a lot of queuing downstream from us,
|
|
|
|
|
* which means that we can push a lot of data into a connection
|
|
|
|
|
* that has stalled and won't ever recover.
|
|
|
|
|
*/
|
|
|
|
|
reconnect_activity(s->reconnect, time_msec());
|
|
|
|
|
}
|
|
|
|
|
|
2009-12-02 10:50:18 -08:00
|
|
|
|
error = jsonrpc_get_status(s->rpc);
|
|
|
|
|
if (error) {
|
2010-06-11 14:39:13 -07:00
|
|
|
|
reconnect_disconnected(s->reconnect, time_msec(), error);
|
2009-12-02 10:50:18 -08:00
|
|
|
|
jsonrpc_session_disconnect(s);
|
2013-03-15 16:14:28 -07:00
|
|
|
|
s->last_error = error;
|
2009-12-02 10:50:18 -08:00
|
|
|
|
}
|
|
|
|
|
} else if (s->stream) {
|
2010-01-06 14:26:48 -08:00
|
|
|
|
int error;
|
|
|
|
|
|
|
|
|
|
stream_run(s->stream);
|
|
|
|
|
error = stream_connect(s->stream);
|
2009-12-02 10:50:18 -08:00
|
|
|
|
if (!error) {
|
|
|
|
|
reconnect_connected(s->reconnect, time_msec());
|
|
|
|
|
s->rpc = jsonrpc_open(s->stream);
|
2020-10-21 03:32:49 +02:00
|
|
|
|
jsonrpc_set_backlog_threshold(s->rpc, s->max_n_msgs,
|
|
|
|
|
s->max_backlog_bytes);
|
2009-12-02 10:50:18 -08:00
|
|
|
|
s->stream = NULL;
|
2017-10-04 23:38:43 -07:00
|
|
|
|
s->seqno++;
|
2009-12-02 10:50:18 -08:00
|
|
|
|
} else if (error != EAGAIN) {
|
|
|
|
|
reconnect_connect_failed(s->reconnect, time_msec(), error);
|
2018-01-22 11:09:40 -08:00
|
|
|
|
jsonrpc_session_pick_remote(s);
|
2009-12-02 10:50:18 -08:00
|
|
|
|
stream_close(s->stream);
|
|
|
|
|
s->stream = NULL;
|
2015-08-19 15:42:07 -07:00
|
|
|
|
s->last_error = error;
|
2009-12-02 10:50:18 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (reconnect_run(s->reconnect, time_msec())) {
|
|
|
|
|
case RECONNECT_CONNECT:
|
|
|
|
|
jsonrpc_session_connect(s);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case RECONNECT_DISCONNECT:
|
2010-01-08 15:36:06 -08:00
|
|
|
|
reconnect_disconnected(s->reconnect, time_msec(), 0);
|
2009-12-02 10:50:18 -08:00
|
|
|
|
jsonrpc_session_disconnect(s);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case RECONNECT_PROBE:
|
|
|
|
|
if (s->rpc) {
|
|
|
|
|
struct json *params;
|
|
|
|
|
struct jsonrpc_msg *request;
|
|
|
|
|
|
|
|
|
|
params = json_array_create_empty();
|
2009-12-01 16:32:03 -08:00
|
|
|
|
request = jsonrpc_create_request("echo", params, NULL);
|
2009-12-02 10:50:18 -08:00
|
|
|
|
json_destroy(request->id);
|
|
|
|
|
request->id = json_string_create("echo");
|
|
|
|
|
jsonrpc_send(s->rpc, request);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
jsonrpc_session_wait(struct jsonrpc_session *s)
|
|
|
|
|
{
|
|
|
|
|
if (s->rpc) {
|
|
|
|
|
jsonrpc_wait(s->rpc);
|
|
|
|
|
} else if (s->stream) {
|
2010-01-06 14:26:48 -08:00
|
|
|
|
stream_run_wait(s->stream);
|
2009-12-02 10:50:18 -08:00
|
|
|
|
stream_connect_wait(s->stream);
|
|
|
|
|
}
|
2010-03-24 10:14:39 -07:00
|
|
|
|
if (s->pstream) {
|
|
|
|
|
pstream_wait(s->pstream);
|
|
|
|
|
}
|
2009-12-02 10:50:18 -08:00
|
|
|
|
reconnect_wait(s->reconnect, time_msec());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t
|
|
|
|
|
jsonrpc_session_get_backlog(const struct jsonrpc_session *s)
|
|
|
|
|
{
|
|
|
|
|
return s->rpc ? jsonrpc_get_backlog(s->rpc) : 0;
|
|
|
|
|
}
|
|
|
|
|
|
2011-01-28 15:39:55 -08:00
|
|
|
|
/* Always returns a pointer to a valid C string, assuming 's' was initialized
|
|
|
|
|
* correctly. */
|
2009-12-02 10:50:18 -08:00
|
|
|
|
const char *
|
|
|
|
|
jsonrpc_session_get_name(const struct jsonrpc_session *s)
|
|
|
|
|
{
|
|
|
|
|
return reconnect_get_name(s->reconnect);
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-31 19:04:32 -04:00
|
|
|
|
const char *
|
|
|
|
|
jsonrpc_session_get_id(const struct jsonrpc_session *s)
|
|
|
|
|
{
|
|
|
|
|
if (s->rpc && s->rpc->stream) {
|
|
|
|
|
return stream_get_peer_id(s->rpc->stream);
|
|
|
|
|
} else {
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-22 11:09:40 -08:00
|
|
|
|
size_t
|
|
|
|
|
jsonrpc_session_get_n_remotes(const struct jsonrpc_session *s)
|
|
|
|
|
{
|
|
|
|
|
return s->remotes.n;
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-12 10:51:30 -08:00
|
|
|
|
/* Always takes ownership of 'msg', regardless of success. */
|
2009-12-02 10:50:18 -08:00
|
|
|
|
int
|
|
|
|
|
jsonrpc_session_send(struct jsonrpc_session *s, struct jsonrpc_msg *msg)
|
|
|
|
|
{
|
2010-01-12 10:51:30 -08:00
|
|
|
|
if (s->rpc) {
|
|
|
|
|
return jsonrpc_send(s->rpc, msg);
|
|
|
|
|
} else {
|
|
|
|
|
jsonrpc_msg_destroy(msg);
|
|
|
|
|
return ENOTCONN;
|
|
|
|
|
}
|
2009-12-02 10:50:18 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct jsonrpc_msg *
|
|
|
|
|
jsonrpc_session_recv(struct jsonrpc_session *s)
|
|
|
|
|
{
|
|
|
|
|
if (s->rpc) {
|
2012-09-05 13:34:35 -07:00
|
|
|
|
unsigned int received_bytes;
|
2009-12-17 15:16:43 -08:00
|
|
|
|
struct jsonrpc_msg *msg;
|
2012-09-05 13:34:35 -07:00
|
|
|
|
|
|
|
|
|
received_bytes = jsonrpc_get_received_bytes(s->rpc);
|
2009-12-02 10:50:18 -08:00
|
|
|
|
jsonrpc_recv(s->rpc, &msg);
|
jsonrpc: Avoid disconnecting prematurely due to long poll intervals.
Open vSwitch has a few different jsonrpc-based protocols that depend on
jsonrpc_session to make sure that the connection is up and working.
In turn, jsonrpc_session uses the "reconnect" state machine to send
probes if nothing is received. This works fine in normal circumstances.
In unusual circumstances, though, it can happen that the program is
busy and doesn't even try to receive anything for a long time. Then the
timer can time out without a good reason; if it had tried to receive
something, it would have.
There's a solution that the clients of jsonrpc_session could adopt.
Instead of first calling jsonrpc_session_run(), which is what calls into
"reconnect" to deal with timing out, and then calling into
jsonrpc_session_recv(), which is what tries to receive something, they
could use the opposite order. That would make sure that the timeout
was always based on a recent attempt to receive something. Great.
The actual code in OVS that uses jsonrpc_session, though, tends to use
the opposite order, and there are enough users and this is a subtle
enough issue that it could get flipped back around even if we fixed it
now. So this commit takes a different approach. Instead of fixing
this in the users of jsonrpc_session, we fix it in the users of
reconnect: make them tell when they've tried to receive something (or
disable this particular feature).
This commit fixes the problem that way. It's kind of hard to reproduce
but I'm pretty sure that I've seen it a number of times in testing.
Signed-off-by: Ben Pfaff <blp@ovn.org>
Acked-by: Ilya Maximets <i.maximets@ovn.org>
2020-11-18 16:39:46 -08:00
|
|
|
|
|
|
|
|
|
long long int now = time_msec();
|
|
|
|
|
reconnect_receive_attempted(s->reconnect, now);
|
2012-09-05 13:34:35 -07:00
|
|
|
|
if (received_bytes != jsonrpc_get_received_bytes(s->rpc)) {
|
|
|
|
|
/* Data was successfully received.
|
|
|
|
|
*
|
|
|
|
|
* Previously we only counted receiving a full message as activity,
|
|
|
|
|
* but with large messages or a slow connection that policy could
|
|
|
|
|
* time out the session mid-message. */
|
jsonrpc: Avoid disconnecting prematurely due to long poll intervals.
Open vSwitch has a few different jsonrpc-based protocols that depend on
jsonrpc_session to make sure that the connection is up and working.
In turn, jsonrpc_session uses the "reconnect" state machine to send
probes if nothing is received. This works fine in normal circumstances.
In unusual circumstances, though, it can happen that the program is
busy and doesn't even try to receive anything for a long time. Then the
timer can time out without a good reason; if it had tried to receive
something, it would have.
There's a solution that the clients of jsonrpc_session could adopt.
Instead of first calling jsonrpc_session_run(), which is what calls into
"reconnect" to deal with timing out, and then calling into
jsonrpc_session_recv(), which is what tries to receive something, they
could use the opposite order. That would make sure that the timeout
was always based on a recent attempt to receive something. Great.
The actual code in OVS that uses jsonrpc_session, though, tends to use
the opposite order, and there are enough users and this is a subtle
enough issue that it could get flipped back around even if we fixed it
now. So this commit takes a different approach. Instead of fixing
this in the users of jsonrpc_session, we fix it in the users of
reconnect: make them tell when they've tried to receive something (or
disable this particular feature).
This commit fixes the problem that way. It's kind of hard to reproduce
but I'm pretty sure that I've seen it a number of times in testing.
Signed-off-by: Ben Pfaff <blp@ovn.org>
Acked-by: Ilya Maximets <i.maximets@ovn.org>
2020-11-18 16:39:46 -08:00
|
|
|
|
reconnect_activity(s->reconnect, now);
|
2012-09-05 13:34:35 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (msg) {
|
2009-12-17 15:16:43 -08:00
|
|
|
|
if (msg->type == JSONRPC_REQUEST && !strcmp(msg->method, "echo")) {
|
|
|
|
|
/* Echo request. Send reply. */
|
|
|
|
|
struct jsonrpc_msg *reply;
|
|
|
|
|
|
|
|
|
|
reply = jsonrpc_create_reply(json_clone(msg->params), msg->id);
|
|
|
|
|
jsonrpc_session_send(s, reply);
|
|
|
|
|
} else if (msg->type == JSONRPC_REPLY
|
2010-08-20 22:26:25 -07:00
|
|
|
|
&& msg->id && msg->id->type == JSON_STRING
|
2025-06-24 21:54:31 +02:00
|
|
|
|
&& !strcmp(json_string(msg->id), "echo")) {
|
2009-12-17 15:16:43 -08:00
|
|
|
|
/* It's a reply to our echo request. Suppress it. */
|
|
|
|
|
} else {
|
|
|
|
|
return msg;
|
|
|
|
|
}
|
|
|
|
|
jsonrpc_msg_destroy(msg);
|
2009-12-02 10:50:18 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
2009-12-17 15:16:43 -08:00
|
|
|
|
return NULL;
|
2009-12-02 10:50:18 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
jsonrpc_session_recv_wait(struct jsonrpc_session *s)
|
|
|
|
|
{
|
|
|
|
|
if (s->rpc) {
|
|
|
|
|
jsonrpc_recv_wait(s->rpc);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-31 11:28:27 -07:00
|
|
|
|
/* Returns true if 's' is currently connected or trying to connect. */
|
2009-12-17 15:16:43 -08:00
|
|
|
|
bool
|
|
|
|
|
jsonrpc_session_is_alive(const struct jsonrpc_session *s)
|
|
|
|
|
{
|
|
|
|
|
return s->rpc || s->stream || reconnect_get_max_tries(s->reconnect);
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-31 11:28:27 -07:00
|
|
|
|
/* Returns true if 's' is currently connected. */
|
2009-12-02 10:50:18 -08:00
|
|
|
|
bool
|
|
|
|
|
jsonrpc_session_is_connected(const struct jsonrpc_session *s)
|
|
|
|
|
{
|
|
|
|
|
return s->rpc != NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-31 11:28:27 -07:00
|
|
|
|
/* Returns a sequence number for 's'. The sequence number increments every
|
|
|
|
|
* time 's' connects or disconnects. Thus, a caller can use the change (or
|
|
|
|
|
* lack of change) in the sequence number to figure out whether the underlying
|
|
|
|
|
* connection is the same as before. */
|
2009-12-02 10:50:18 -08:00
|
|
|
|
unsigned int
|
|
|
|
|
jsonrpc_session_get_seqno(const struct jsonrpc_session *s)
|
|
|
|
|
{
|
|
|
|
|
return s->seqno;
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-31 11:28:27 -07:00
|
|
|
|
/* Returns the current status of 's'. If 's' is NULL or is disconnected, this
|
|
|
|
|
* is 0, otherwise it is the status of the connection, as reported by
|
|
|
|
|
* jsonrpc_get_status(). */
|
2011-01-28 15:39:55 -08:00
|
|
|
|
int
|
|
|
|
|
jsonrpc_session_get_status(const struct jsonrpc_session *s)
|
|
|
|
|
{
|
|
|
|
|
return s && s->rpc ? jsonrpc_get_status(s->rpc) : 0;
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-31 11:28:27 -07:00
|
|
|
|
/* Returns the last error reported on a connection by 's'. The return value is
|
|
|
|
|
* 0 only if no connection made by 's' has ever encountered an error. See
|
|
|
|
|
* jsonrpc_get_status() for return value interpretation. */
|
2013-03-15 16:14:28 -07:00
|
|
|
|
int
|
|
|
|
|
jsonrpc_session_get_last_error(const struct jsonrpc_session *s)
|
|
|
|
|
{
|
|
|
|
|
return s->last_error;
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-31 11:28:27 -07:00
|
|
|
|
/* Populates 'stats' with statistics from 's'. */
|
2011-01-28 15:39:55 -08:00
|
|
|
|
void
|
|
|
|
|
jsonrpc_session_get_reconnect_stats(const struct jsonrpc_session *s,
|
|
|
|
|
struct reconnect_stats *stats)
|
|
|
|
|
{
|
|
|
|
|
reconnect_get_stats(s->reconnect, time_msec(), stats);
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-31 11:28:27 -07:00
|
|
|
|
/* Enables 's' to reconnect to the peer if the connection drops. */
|
2014-02-18 13:19:36 -08:00
|
|
|
|
void
|
|
|
|
|
jsonrpc_session_enable_reconnect(struct jsonrpc_session *s)
|
|
|
|
|
{
|
|
|
|
|
reconnect_set_max_tries(s->reconnect, UINT_MAX);
|
|
|
|
|
reconnect_set_backoff(s->reconnect, RECONNECT_DEFAULT_MIN_BACKOFF,
|
|
|
|
|
RECONNECT_DEFAULT_MAX_BACKOFF);
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-31 11:28:27 -07:00
|
|
|
|
/* Forces 's' to drop its connection (if any) and reconnect. */
|
2009-12-02 10:50:18 -08:00
|
|
|
|
void
|
|
|
|
|
jsonrpc_session_force_reconnect(struct jsonrpc_session *s)
|
|
|
|
|
{
|
|
|
|
|
reconnect_force_reconnect(s->reconnect, time_msec());
|
|
|
|
|
}
|
2010-11-05 10:22:18 -07:00
|
|
|
|
|
ovsdb-cs: Perform forced reconnects without a backoff.
The ovsdb-cs layer triggers a forced reconnect in various cases:
- when an inconsistency is detected in the data received from the
remote server.
- when the remote server is running in clustered mode and transitioned
to "follower", if the client is configured in "leader-only" mode.
- when explicitly requested by upper layers (e.g., by the user
application, through the IDL layer).
In such cases it's desirable that reconnection should happen as fast as
possible, without the current exponential backoff maintained by the
underlying reconnect object. Furthermore, since 3c2d6274bcee ("raft:
Transfer leadership before creating snapshots."), leadership changes
inside the clustered database happen more often and, therefore,
"leader-only" clients need to reconnect more often too.
Forced reconnects call jsonrpc_session_force_reconnect() which will not
reset backoff. To make sure clients reconnect as fast as possible in
the aforementioned scenarios we first call the new API,
jsonrpc_session_reset_backoff(), in ovsdb-cs, for sessions that are in
state CS_S_MONITORING (i.e., the remote is likely still alive and
functioning fine).
jsonrpc_session_reset_backoff() resets the number of backoff-free
reconnect retries to the number of remotes configured for the session,
ensuring that all remotes are retried exactly once with backoff 0.
This commit also updates the Python IDL and jsonrpc implementations.
The Python IDL wasn't tracking the IDL_S_MONITORING state explicitly,
we now do that too. Tests were also added to make sure the IDL forced
reconnects happen without backoff.
Reported-at: https://bugzilla.redhat.com/1977264
Suggested-by: Ilya Maximets <i.maximets@ovn.org>
Signed-off-by: Dumitru Ceara <dceara@redhat.com>
Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
2021-07-21 14:51:00 +02:00
|
|
|
|
/* Resets the reconnect backoff for 's' by allowing as many free tries as the
|
|
|
|
|
* number of configured remotes. This is to be used by upper layers before
|
|
|
|
|
* calling jsonrpc_session_force_reconnect() if backoff is undesirable.
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
jsonrpc_session_reset_backoff(struct jsonrpc_session *s)
|
|
|
|
|
{
|
|
|
|
|
unsigned int free_tries = s->remotes.n;
|
|
|
|
|
|
|
|
|
|
if (jsonrpc_session_is_connected(s)) {
|
|
|
|
|
/* The extra free try will be consumed when the current remote
|
|
|
|
|
* is disconnected.
|
|
|
|
|
*/
|
|
|
|
|
free_tries++;
|
|
|
|
|
}
|
|
|
|
|
reconnect_set_backoff_free_tries(s->reconnect, free_tries);
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-31 11:28:27 -07:00
|
|
|
|
/* Sets 'max_backoff' as the maximum time, in milliseconds, to wait after a
|
|
|
|
|
* connection attempt fails before attempting to connect again. */
|
2010-11-05 10:22:18 -07:00
|
|
|
|
void
|
|
|
|
|
jsonrpc_session_set_max_backoff(struct jsonrpc_session *s, int max_backoff)
|
|
|
|
|
{
|
|
|
|
|
reconnect_set_backoff(s->reconnect, 0, max_backoff);
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-31 11:28:27 -07:00
|
|
|
|
/* Sets the "probe interval" for 's' to 'probe_interval', in milliseconds. If
|
|
|
|
|
* this is zero, it disables the connection keepalive feature. Otherwise, if
|
|
|
|
|
* 's' is idle for 'probe_interval' milliseconds then 's' will send an echo
|
|
|
|
|
* request and, if no reply is received within an additional 'probe_interval'
|
|
|
|
|
* milliseconds, close the connection (then reconnect, if that feature is
|
|
|
|
|
* enabled). */
|
2010-11-05 10:22:18 -07:00
|
|
|
|
void
|
|
|
|
|
jsonrpc_session_set_probe_interval(struct jsonrpc_session *s,
|
|
|
|
|
int probe_interval)
|
|
|
|
|
{
|
2021-05-27 15:29:04 +02:00
|
|
|
|
if (ovs_replay_is_active()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2010-11-05 10:22:18 -07:00
|
|
|
|
reconnect_set_probe_interval(s->reconnect, probe_interval);
|
|
|
|
|
}
|
2012-03-10 15:58:10 -08:00
|
|
|
|
|
2015-03-31 11:28:27 -07:00
|
|
|
|
/* Sets the DSCP value used for 's''s connection to 'dscp'. If this is
|
|
|
|
|
* different from the DSCP value currently in use then the connection is closed
|
|
|
|
|
* and reconnected. */
|
2012-03-10 15:58:10 -08:00
|
|
|
|
void
|
2015-03-31 11:28:27 -07:00
|
|
|
|
jsonrpc_session_set_dscp(struct jsonrpc_session *s, uint8_t dscp)
|
2012-03-10 15:58:10 -08:00
|
|
|
|
{
|
2012-06-21 12:22:42 -07:00
|
|
|
|
if (s->dscp != dscp) {
|
2015-02-20 08:44:48 -08:00
|
|
|
|
pstream_close(s->pstream);
|
|
|
|
|
s->pstream = NULL;
|
2012-10-04 12:33:05 -07:00
|
|
|
|
|
2012-06-21 12:22:42 -07:00
|
|
|
|
s->dscp = dscp;
|
|
|
|
|
jsonrpc_session_force_reconnect(s);
|
|
|
|
|
}
|
2012-03-10 15:58:10 -08:00
|
|
|
|
}
|
2020-10-21 03:32:49 +02:00
|
|
|
|
|
2024-01-09 23:49:10 +01:00
|
|
|
|
void
|
|
|
|
|
jsonrpc_session_set_options(struct jsonrpc_session *s,
|
|
|
|
|
const struct jsonrpc_session_options *options)
|
|
|
|
|
{
|
|
|
|
|
jsonrpc_session_set_max_backoff(s, options->max_backoff);
|
|
|
|
|
jsonrpc_session_set_probe_interval(s, options->probe_interval);
|
|
|
|
|
jsonrpc_session_set_dscp(s, options->dscp);
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-21 03:32:49 +02:00
|
|
|
|
/* Sets thresholds for send backlog. If send backlog contains more than
|
|
|
|
|
* 'max_n_msgs' messages or is larger than 'max_backlog_bytes' bytes,
|
|
|
|
|
* connection will be closed (then reconnected, if that feature is enabled). */
|
|
|
|
|
void
|
|
|
|
|
jsonrpc_session_set_backlog_threshold(struct jsonrpc_session *s,
|
|
|
|
|
size_t max_n_msgs,
|
|
|
|
|
size_t max_backlog_bytes)
|
|
|
|
|
{
|
|
|
|
|
s->max_n_msgs = max_n_msgs;
|
|
|
|
|
s->max_backlog_bytes = max_backlog_bytes;
|
|
|
|
|
if (s->rpc) {
|
|
|
|
|
jsonrpc_set_backlog_threshold(s->rpc, max_n_msgs, max_backlog_bytes);
|
|
|
|
|
}
|
|
|
|
|
}
|