+ struct pstream *listener; /* Listener, if passive. */
+ struct list sessions; /* List of "struct ovsdb_jsonrpc_session"s. */
+};
+
+static void ovsdb_jsonrpc_server_add_remote(struct ovsdb_jsonrpc_server *,
+ const char *name);
+static void ovsdb_jsonrpc_server_del_remote(struct shash_node *);
+
+struct ovsdb_jsonrpc_server *
+ovsdb_jsonrpc_server_create(struct ovsdb *db)
+{
+ struct ovsdb_jsonrpc_server *server = xzalloc(sizeof *server);
+ server->db = db;
+ server->max_sessions = 64;
+ shash_init(&server->remotes);
+ return server;
+}
+
+/* Sets 'svr''s current set of remotes to the names in 'new_remotes'. The data
+ * values in 'new_remotes' are ignored.
+ *
+ * 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)) {
+ ovsdb_jsonrpc_server_del_remote(node);
+ }
+ }
+ SHASH_FOR_EACH (node, new_remotes) {
+ if (!shash_find(&svr->remotes, node->name)) {
+ ovsdb_jsonrpc_server_add_remote(svr, node->name);
+ }
+ }
+}
+
+static void
+ovsdb_jsonrpc_server_add_remote(struct ovsdb_jsonrpc_server *svr,
+ const char *name)
+{
+ struct ovsdb_jsonrpc_remote *remote;
+ struct pstream *listener;
+ int error;
+
+ error = pstream_open(name, &listener);
+ if (error && error != EAFNOSUPPORT) {
+ VLOG_ERR_RL(&rl, "%s: listen failed: %s", name, strerror(error));
+ return;
+ }
+
+ remote = xmalloc(sizeof *remote);
+ remote->server = svr;
+ remote->listener = listener;
+ list_init(&remote->sessions);
+ shash_add(&svr->remotes, name, remote);
+
+ if (!listener) {
+ ovsdb_jsonrpc_session_create(remote, jsonrpc_session_open(name));
+ }
+}
+
+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);
+}
+
+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));
+ 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);
+ }
+}
+\f
+/* JSON-RPC database server session. */
+
+struct ovsdb_jsonrpc_session {
+ struct ovsdb_jsonrpc_remote *remote;
+ struct list node; /* Element in remote's sessions list. */
+
+ /* Triggers. */
+ struct hmap triggers; /* Hmap of "struct ovsdb_jsonrpc_trigger"s. */