ovsdb-server: Fix a reference count leak bug
[cascardo/ovs.git] / ovsdb / jsonrpc-server.c
index ee2026e..caaf2bf 100644 (file)
@@ -63,9 +63,12 @@ static void ovsdb_jsonrpc_session_close_all(struct ovsdb_jsonrpc_remote *);
 static void ovsdb_jsonrpc_session_reconnect_all(struct ovsdb_jsonrpc_remote *);
 static void ovsdb_jsonrpc_session_set_all_options(
     struct ovsdb_jsonrpc_remote *, const struct ovsdb_jsonrpc_options *);
-static bool ovsdb_jsonrpc_session_get_status(
+static bool ovsdb_jsonrpc_active_session_get_status(
     const struct ovsdb_jsonrpc_remote *,
     struct ovsdb_jsonrpc_remote_status *);
+static void ovsdb_jsonrpc_session_get_status(
+    const struct ovsdb_jsonrpc_session *,
+    struct ovsdb_jsonrpc_remote_status *);
 static void ovsdb_jsonrpc_session_unlock_all(struct ovsdb_jsonrpc_session *);
 static void ovsdb_jsonrpc_session_unlock__(struct ovsdb_lock_waiter *);
 static void ovsdb_jsonrpc_session_send(struct ovsdb_jsonrpc_session *,
@@ -101,7 +104,7 @@ static struct json *ovsdb_jsonrpc_monitor_compose_update(
 
 struct ovsdb_jsonrpc_server {
     struct ovsdb_server up;
-    unsigned int n_sessions, max_sessions;
+    unsigned int n_sessions;
     struct shash remotes;      /* Contains "struct ovsdb_jsonrpc_remote *"s. */
 };
 
@@ -130,7 +133,6 @@ ovsdb_jsonrpc_server_create(void)
 {
     struct ovsdb_jsonrpc_server *server = xzalloc(sizeof *server);
     ovsdb_server_init(&server->up);
-    server->max_sessions = 330;   /* Random limit. */
     shash_init(&server->remotes);
     return server;
 }
@@ -276,7 +278,7 @@ ovsdb_jsonrpc_server_del_remote(struct shash_node *node)
 /* 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
+ * a remote named 'target' or if that remote is an outbound remote that has no
  * active connections) returns false.  On failure, 'status' will be zeroed.
  */
 bool
@@ -289,7 +291,19 @@ ovsdb_jsonrpc_server_get_remote_status(
     memset(status, 0, sizeof *status);
 
     remote = shash_find_data(&svr->remotes, target);
-    return remote && ovsdb_jsonrpc_session_get_status(remote, status);
+
+    if (!remote) {
+        return false;
+    }
+
+    if (remote->listener) {
+        status->bound_port = pstream_get_bound_port(remote->listener);
+        status->is_connected = !list_is_empty(&remote->sessions);
+        status->n_connections = list_size(&remote->sessions);
+        return true;
+    }
+
+    return ovsdb_jsonrpc_active_session_get_status(remote, status);
 }
 
 void
@@ -324,25 +338,19 @@ ovsdb_jsonrpc_server_run(struct ovsdb_jsonrpc_server *svr)
         struct ovsdb_jsonrpc_remote *remote = node->data;
 
         if (remote->listener) {
-            if (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),
-                                 ovs_strerror(error));
-                }
-            } else {
-                VLOG_WARN_RL(&rl, "%s: connection exceeded maximum (%d)",
+            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),
-                             svr->max_sessions);
+                             ovs_strerror(error));
             }
         }
 
@@ -358,7 +366,7 @@ ovsdb_jsonrpc_server_wait(struct ovsdb_jsonrpc_server *svr)
     SHASH_FOR_EACH (node, &svr->remotes) {
         struct ovsdb_jsonrpc_remote *remote = node->data;
 
-        if (remote->listener && svr->n_sessions < svr->max_sessions) {
+        if (remote->listener) {
             pstream_wait(remote->listener);
         }
 
@@ -590,24 +598,37 @@ ovsdb_jsonrpc_session_set_all_options(
     }
 }
 
+/* Sets the 'status' of for the 'remote' with an outgoing connection.   */
 static bool
-ovsdb_jsonrpc_session_get_status(const struct ovsdb_jsonrpc_remote *remote,
-                                 struct ovsdb_jsonrpc_remote_status *status)
+ovsdb_jsonrpc_active_session_get_status(
+    const struct ovsdb_jsonrpc_remote *remote,
+    struct ovsdb_jsonrpc_remote_status *status)
 {
+    const struct ovs_list *sessions = &remote->sessions;
     const struct ovsdb_jsonrpc_session *s;
+
+    if (list_is_empty(sessions)) {
+        return false;
+    }
+
+    ovs_assert(list_is_singleton(sessions));
+    s = CONTAINER_OF(list_front(sessions), struct ovsdb_jsonrpc_session, node);
+    ovsdb_jsonrpc_session_get_status(s, status);
+    status->n_connections = 1;
+
+    return true;
+}
+
+static void
+ovsdb_jsonrpc_session_get_status(const struct ovsdb_jsonrpc_session *session,
+                                 struct ovsdb_jsonrpc_remote_status *status)
+{
+    const struct ovsdb_jsonrpc_session *s = session;
     const struct jsonrpc_session *js;
     struct ovsdb_lock_waiter *waiter;
     struct reconnect_stats rstats;
     struct ds locks_held, locks_waiting, locks_lost;
 
-    status->bound_port = (remote->listener
-                          ? pstream_get_bound_port(remote->listener)
-                          : htons(0));
-
-    if (list_is_empty(&remote->sessions)) {
-        return false;
-    }
-    s = CONTAINER_OF(remote->sessions.next, struct ovsdb_jsonrpc_session, node);
     js = s->js;
 
     status->is_connected = jsonrpc_session_is_connected(js);
@@ -637,10 +658,6 @@ ovsdb_jsonrpc_session_get_status(const struct ovsdb_jsonrpc_remote *remote,
     status->locks_held = ds_steal_cstr(&locks_held);
     status->locks_waiting = ds_steal_cstr(&locks_waiting);
     status->locks_lost = ds_steal_cstr(&locks_lost);
-
-    status->n_connections = list_size(&remote->sessions);
-
-    return true;
 }
 
 /* Examines 'request' to determine the database to which it relates, and then
@@ -1248,7 +1265,7 @@ ovsdb_jsonrpc_monitor_create(struct ovsdb_jsonrpc_session *s, struct ovsdb *db,
     dbmon = ovsdb_monitor_add(m->dbmon);
     if (dbmon != m->dbmon) {
         /* Found an exisiting dbmon, reuse the current one. */
-        ovsdb_monitor_remove_jsonrpc_monitor(m->dbmon, m);
+        ovsdb_monitor_remove_jsonrpc_monitor(m->dbmon, m, m->unflushed);
         ovsdb_monitor_add_jsonrpc_monitor(dbmon, m);
         m->dbmon = dbmon;
     }
@@ -1304,6 +1321,10 @@ static struct json *
 ovsdb_jsonrpc_monitor_compose_update(struct ovsdb_jsonrpc_monitor *m,
                                      bool initial)
 {
+    if (!ovsdb_monitor_needs_flush(m->dbmon, m->unflushed)) {
+        return NULL;
+    }
+
     return ovsdb_monitor_get_update(m->dbmon, initial, &m->unflushed,
                                     m->version);
 }
@@ -1327,7 +1348,7 @@ ovsdb_jsonrpc_monitor_destroy(struct ovsdb_jsonrpc_monitor *m)
 {
     json_destroy(m->monitor_id);
     hmap_remove(&m->session->monitors, &m->node);
-    ovsdb_monitor_remove_jsonrpc_monitor(m->dbmon, m);
+    ovsdb_monitor_remove_jsonrpc_monitor(m->dbmon, m, m->unflushed);
     free(m);
 }