From: Serge Hallyn Date: Tue, 11 Mar 2014 02:41:34 +0000 (-0500) Subject: cgmanager: avoid stray dbus connections X-Git-Tag: lxc-1.1.0.alpha1~214 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a2868fe90b37f6d22cae1cff1574bac85d6a9891;p=thirdparty%2Flxc.git cgmanager: avoid stray dbus connections 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 Acked-by: Stéphane Graber --- diff --git a/src/lxc/cgmanager.c b/src/lxc/cgmanager.c index 4795da43a..38f482b94 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; }