+ struct pstream *listener; /* Listener, if passive. */
+ struct list sessions; /* List of "struct ovsdb_jsonrpc_session"s. */
+ uint8_t dscp;
+};
+
+static struct ovsdb_jsonrpc_remote *ovsdb_jsonrpc_server_add_remote(
+ struct ovsdb_jsonrpc_server *, const char *name,
+ const struct ovsdb_jsonrpc_options *options
+);
+static void ovsdb_jsonrpc_server_del_remote(struct shash_node *);
+
+/* Creates and returns a new server to provide JSON-RPC access to an OVSDB.
+ *
+ * The caller must call ovsdb_jsonrpc_server_add_db() for each database to
+ * which 'server' should provide access. */
+struct ovsdb_jsonrpc_server *
+ovsdb_jsonrpc_server_create(void)
+{
+ struct ovsdb_jsonrpc_server *server = xzalloc(sizeof *server);
+ ovsdb_server_init(&server->up);
+ server->max_sessions = 64;
+ shash_init(&server->remotes);
+ return server;
+}
+
+/* Adds 'db' to the set of databases served out by 'svr'. Returns true if
+ * successful, false if 'db''s name is the same as some database already in
+ * 'server'. */
+bool
+ovsdb_jsonrpc_server_add_db(struct ovsdb_jsonrpc_server *svr, struct ovsdb *db)
+{
+ return ovsdb_server_add_db(&svr->up, db);
+}
+
+void
+ovsdb_jsonrpc_server_destroy(struct ovsdb_jsonrpc_server *svr)
+{
+ struct shash_node *node, *next;
+
+ SHASH_FOR_EACH_SAFE (node, next, &svr->remotes) {
+ ovsdb_jsonrpc_server_del_remote(node);
+ }
+ shash_destroy(&svr->remotes);
+ ovsdb_server_destroy(&svr->up);
+ free(svr);
+}
+
+struct ovsdb_jsonrpc_options *
+ovsdb_jsonrpc_default_options(const char *target)
+{
+ struct ovsdb_jsonrpc_options *options = xzalloc(sizeof *options);
+ options->max_backoff = RECONNECT_DEFAULT_MAX_BACKOFF;
+ options->probe_interval = (stream_or_pstream_needs_probes(target)
+ ? RECONNECT_DEFAULT_PROBE_INTERVAL
+ : 0);
+ return options;
+}
+
+/* Sets 'svr''s current set of remotes to the names in 'new_remotes', with
+ * options in the struct ovsdb_jsonrpc_options supplied as the data values.
+ *
+ * A remote is an active or passive stream connection method, e.g. "pssl:" or
+ * "tcp:1.2.3.4". */
+void
+ovsdb_jsonrpc_server_set_remotes(struct ovsdb_jsonrpc_server *svr,
+ const struct shash *new_remotes)
+{
+ struct shash_node *node, *next;
+
+ SHASH_FOR_EACH_SAFE (node, next, &svr->remotes) {
+ if (!shash_find(new_remotes, node->name)) {
+ VLOG_INFO("%s: remote deconfigured", node->name);
+ ovsdb_jsonrpc_server_del_remote(node);
+ }
+ }
+ SHASH_FOR_EACH (node, new_remotes) {
+ const struct ovsdb_jsonrpc_options *options = node->data;
+ struct ovsdb_jsonrpc_remote *remote;
+
+ remote = shash_find_data(&svr->remotes, node->name);
+ if (!remote) {
+ remote = ovsdb_jsonrpc_server_add_remote(svr, node->name, options);
+ if (!remote) {
+ continue;
+ }
+ }
+
+ ovsdb_jsonrpc_session_set_all_options(remote, options);
+ }
+}
+
+static struct ovsdb_jsonrpc_remote *
+ovsdb_jsonrpc_server_add_remote(struct ovsdb_jsonrpc_server *svr,
+ const char *name,
+ const struct ovsdb_jsonrpc_options *options)
+{
+ struct ovsdb_jsonrpc_remote *remote;
+ struct pstream *listener;
+ int error;
+
+ error = jsonrpc_pstream_open(name, &listener, options->dscp);
+ if (error && error != EAFNOSUPPORT) {
+ VLOG_ERR_RL(&rl, "%s: listen failed: %s", name, strerror(error));
+ return NULL;
+ }
+
+ remote = xmalloc(sizeof *remote);
+ remote->server = svr;
+ remote->listener = listener;
+ list_init(&remote->sessions);
+ remote->dscp = options->dscp;
+ shash_add(&svr->remotes, name, remote);
+
+ if (!listener) {
+ ovsdb_jsonrpc_session_create(remote, jsonrpc_session_open(name, true));
+ }
+ return remote;
+}
+
+static void
+ovsdb_jsonrpc_server_del_remote(struct shash_node *node)
+{
+ struct ovsdb_jsonrpc_remote *remote = node->data;
+
+ ovsdb_jsonrpc_session_close_all(remote);
+ pstream_close(remote->listener);
+ shash_delete(&remote->server->remotes, node);
+ free(remote);
+}
+
+/* Stores status information for the remote named 'target', which should have
+ * been configured on 'svr' with a call to ovsdb_jsonrpc_server_set_remotes(),
+ * into '*status'. On success returns true, on failure (if 'svr' doesn't have
+ * a remote named 'target' or if that remote is an inbound remote that has no
+ * active connections) returns false. On failure, 'status' will be zeroed.
+ */
+bool
+ovsdb_jsonrpc_server_get_remote_status(
+ const struct ovsdb_jsonrpc_server *svr, const char *target,
+ struct ovsdb_jsonrpc_remote_status *status)
+{
+ const struct ovsdb_jsonrpc_remote *remote;
+
+ memset(status, 0, sizeof *status);
+
+ remote = shash_find_data(&svr->remotes, target);
+ return remote && ovsdb_jsonrpc_session_get_status(remote, status);
+}
+
+void
+ovsdb_jsonrpc_server_free_remote_status(
+ struct ovsdb_jsonrpc_remote_status *status)
+{
+ free(status->locks_held);
+ free(status->locks_waiting);
+ free(status->locks_lost);
+}
+
+/* Forces all of the JSON-RPC sessions managed by 'svr' to disconnect and
+ * reconnect. */
+void
+ovsdb_jsonrpc_server_reconnect(struct ovsdb_jsonrpc_server *svr)
+{
+ struct shash_node *node;
+
+ SHASH_FOR_EACH (node, &svr->remotes) {
+ struct ovsdb_jsonrpc_remote *remote = node->data;
+
+ ovsdb_jsonrpc_session_reconnect_all(remote);
+ }
+}
+
+void
+ovsdb_jsonrpc_server_run(struct ovsdb_jsonrpc_server *svr)
+{
+ struct shash_node *node;
+
+ SHASH_FOR_EACH (node, &svr->remotes) {
+ struct ovsdb_jsonrpc_remote *remote = node->data;
+
+ if (remote->listener && svr->n_sessions < svr->max_sessions) {
+ struct stream *stream;
+ int error;
+
+ error = pstream_accept(remote->listener, &stream);
+ if (!error) {
+ struct jsonrpc_session *js;
+ js = jsonrpc_session_open_unreliably(jsonrpc_open(stream),
+ remote->dscp);
+ ovsdb_jsonrpc_session_create(remote, js);
+ } else if (error != EAGAIN) {
+ VLOG_WARN_RL(&rl, "%s: accept failed: %s",
+ pstream_get_name(remote->listener),
+ strerror(error));
+ }
+ }
+
+ ovsdb_jsonrpc_session_run_all(remote);
+ }
+}
+
+void
+ovsdb_jsonrpc_server_wait(struct ovsdb_jsonrpc_server *svr)
+{
+ struct shash_node *node;
+
+ SHASH_FOR_EACH (node, &svr->remotes) {
+ struct ovsdb_jsonrpc_remote *remote = node->data;
+
+ if (remote->listener && svr->n_sessions < svr->max_sessions) {
+ pstream_wait(remote->listener);
+ }
+
+ ovsdb_jsonrpc_session_wait_all(remote);
+ }
+}
+
+/* Adds some memory usage statistics for 'svr' into 'usage', for use with
+ * memory_report(). */
+void
+ovsdb_jsonrpc_server_get_memory_usage(const struct ovsdb_jsonrpc_server *svr,
+ struct simap *usage)
+{
+ struct shash_node *node;
+
+ simap_increase(usage, "sessions", svr->n_sessions);
+ SHASH_FOR_EACH (node, &svr->remotes) {
+ struct ovsdb_jsonrpc_remote *remote = node->data;
+
+ ovsdb_jsonrpc_session_get_memory_usage_all(remote, usage);
+ }
+}
+\f
+/* JSON-RPC database server session. */
+
+struct ovsdb_jsonrpc_session {
+ struct list node; /* Element in remote's sessions list. */
+ struct ovsdb_session up;
+ struct ovsdb_jsonrpc_remote *remote;
+
+ /* Triggers. */
+ struct hmap triggers; /* Hmap of "struct ovsdb_jsonrpc_trigger"s. */
+
+ /* Monitors. */
+ struct hmap monitors; /* Hmap of "struct ovsdb_jsonrpc_monitor"s. */
+
+ /* Network connectivity. */
+ struct jsonrpc_session *js; /* JSON-RPC session. */
+ unsigned int js_seqno; /* Last jsonrpc_session_get_seqno() value. */