[lxc-devel] [PATCH] cgmanager: avoid stray dbus connections

Serge Hallyn serge.hallyn at ubuntu.com
Tue Mar 11 02:41:34 UTC 2014


There are two parts to this fix.

First, create a private DBusConnection manually, instead of using
nih_dbus_connect.  The latter always creates a shared connection,
which cannot be closed.  Note: creating an actual shared connection,
mutexing it among all threads, and creating per-thread proxies would
be an alternative - however we don't want long-lived connections as
they tend not to be reliable (especially if cgmanager restarts).

Second, use pthread_setspecific to create per-thread keys which can
be associated with destructors.  Specify a destructor which closes
the dbus connection.  If a thread dies while holding cgmanager,
the connection will be closed.  Otherwise, we close the connection
and unset the key.

Signed-off-by: Serge Hallyn <serge.hallyn at ubuntu.com>
---
 src/lxc/cgmanager.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 47 insertions(+), 2 deletions(-)

diff --git a/src/lxc/cgmanager.c b/src/lxc/cgmanager.c
index c80a136..4cf6397 100644
--- a/src/lxc/cgmanager.c
+++ b/src/lxc/cgmanager.c
@@ -70,10 +70,42 @@ struct cgm_data {
 static __thread NihDBusProxy *cgroup_manager = NULL;
 static __thread DBusConnection *connection = NULL;
 static __thread bool cgm_keep_connection = false;
+
+
+struct cgm_key_t {
+	DBusConnection *connection;
+	NihDBusProxy *cgmanager;
+} cgm_key;
+
+void destructor(void *arg) {
+	struct cgm_key_t *cgm_key = arg;
+	nih_free(cgm_key->cgmanager);
+	dbus_connection_flush(cgm_key->connection);
+	dbus_connection_close(cgm_key->connection);
+	dbus_connection_unref(cgm_key->connection);
+}
+static pthread_key_t key;
+static void make_key() {
+	pthread_key_create(&key, destructor);
+}
+
+static void init_cgm_destructor(DBusConnection *c, NihDBusProxy *cgm)
+{
+	static pthread_once_t key_once = PTHREAD_ONCE_INIT;
+	pthread_once(&key_once, make_key);
+	cgm_key.connection = c;
+	cgm_key.cgmanager = cgm;
+	if (!cgm)
+		pthread_setspecific(key, NULL);
+	else if (pthread_getspecific(key) == NULL)
+		pthread_setspecific(key, &cgm_key);
+}
+
 #else
 static NihDBusProxy *cgroup_manager = NULL;
 static DBusConnection *connection = NULL;
 static bool cgm_keep_connection = false;
+static inline void init_cgm_destructor(DBusConnection *c, NihDBusProxy *cgm) { }
 #endif
 
 static struct cgroup_ops cgmanager_ops;
@@ -87,9 +119,13 @@ static void cgm_dbus_disconnect(void)
 	if (cgroup_manager)
 		nih_free(cgroup_manager);
 	cgroup_manager = NULL;
-	if (connection)
+	if (connection) {
+		dbus_connection_flush(connection);
+		dbus_connection_close(connection);
 		dbus_connection_unref(connection);
+	}
 	connection = NULL;
+	init_cgm_destructor(NULL, NULL);
 }
 
 #define CGMANAGER_DBUS_SOCK "unix:path=/sys/fs/cgroup/cgmanager/sock"
@@ -105,14 +141,22 @@ static bool do_cgm_dbus_connect(void)
 
 	dbus_error_init(&dbus_error);
 
-	connection = nih_dbus_connect(CGMANAGER_DBUS_SOCK, NULL);
+	connection = dbus_connection_open_private(CGMANAGER_DBUS_SOCK, &dbus_error);
 	if (!connection) {
+		ERROR("Failed opening dbus connection: %s: %s",
+				dbus_error.name, dbus_error.message);
+		dbus_error_free(&dbus_error);
+		return false;
+	}
+	if (nih_dbus_setup(connection, NULL) < 0) {
 		NihError *nerr;
 		nerr = nih_error_get();
 		DEBUG("Unable to open cgmanager connection at %s: %s", CGMANAGER_DBUS_SOCK,
 			nerr->message);
 		nih_free(nerr);
 		dbus_error_free(&dbus_error);
+		dbus_connection_unref(connection);
+		connection = NULL;
 		return false;
 	}
 	dbus_connection_set_exit_on_disconnect(connection, FALSE);
@@ -138,6 +182,7 @@ static bool do_cgm_dbus_connect(void)
 		cgm_dbus_disconnect();
 		return false;
 	}
+	init_cgm_destructor(connection, cgroup_manager);
 	return true;
 }
 
-- 
1.9.0



More information about the lxc-devel mailing list