#ifdef HAVE_CGMANAGER
lxc_log_define(lxc_cgmanager, lxc);
-static pthread_mutex_t thread_mutex = PTHREAD_MUTEX_INITIALIZER;
-
-static void lock_mutex(pthread_mutex_t *l)
-{
- int ret;
-
- if ((ret = pthread_mutex_lock(l)) != 0) {
- fprintf(stderr, "pthread_mutex_lock returned:%d %s\n", ret, strerror(ret));
- exit(1);
- }
-}
-
-static void unlock_mutex(pthread_mutex_t *l)
-{
- int ret;
-
- if ((ret = pthread_mutex_unlock(l)) != 0) {
- fprintf(stderr, "pthread_mutex_unlock returned:%d %s\n", ret, strerror(ret));
- exit(1);
- }
-}
-
#include <nih-dbus/dbus_connection.h>
#include <cgmanager/cgmanager-client.h>
#include <nih/alloc.h>
const char *cgroup_pattern;
};
+#ifdef HAVE_TLS
+static __thread NihDBusProxy *cgroup_manager = NULL;
+static __thread DBusConnection *connection = NULL;
+static __thread bool cgm_keep_connection = false;
+#else
static NihDBusProxy *cgroup_manager = NULL;
+static DBusConnection *connection = NULL;
+static bool cgm_keep_connection = false;
+#endif
+
static struct cgroup_ops cgmanager_ops;
static int nr_subsystems;
static char **subsystems;
-static DBusConnection *connection;
+
+static void cgm_dbus_disconnect(void)
+{
+ cgm_keep_connection = false;
+ if (cgroup_manager)
+ nih_free(cgroup_manager);
+ cgroup_manager = NULL;
+ if (connection)
+ dbus_connection_unref(connection);
+ connection = NULL;
+}
#define CGMANAGER_DBUS_SOCK "unix:path=/sys/fs/cgroup/cgmanager/sock"
-static bool cgm_dbus_connect(void)
+static bool do_cgm_dbus_connect(void)
{
DBusError dbus_error;
dbus_error_init(&dbus_error);
- lock_mutex(&thread_mutex);
connection = nih_dbus_connect(CGMANAGER_DBUS_SOCK, NULL);
if (!connection) {
NihError *nerr;
nerr->message);
nih_free(nerr);
dbus_error_free(&dbus_error);
- unlock_mutex(&thread_mutex);
return false;
}
dbus_connection_set_exit_on_disconnect(connection, FALSE);
nerr = nih_error_get();
ERROR("Error opening cgmanager proxy: %s", nerr->message);
nih_free(nerr);
- unlock_mutex(&thread_mutex);
+ cgm_dbus_disconnect();
return false;
}
nerr = nih_error_get();
ERROR("Error pinging cgroup manager: %s", nerr->message);
nih_free(nerr);
+ cgm_dbus_disconnect();
}
return true;
}
-static void cgm_dbus_disconnect(void)
+static bool cgm_dbus_connect(void)
{
- if (cgroup_manager)
- nih_free(cgroup_manager);
- cgroup_manager = NULL;
if (connection)
- dbus_connection_unref(connection);
- connection = NULL;
- unlock_mutex(&thread_mutex);
+ return true;
+ return do_cgm_dbus_connect();
}
static int send_creds(int sock, int rpid, int ruid, int rgid)
return 0;
}
-/*
- * Called during container startup. The cgmanager socket is open
- */
static bool lxc_cgmanager_create(const char *controller, const char *cgroup_path, int32_t *existed)
{
+ bool ret = true;
+ if (!cgm_dbus_connect()) {
+ ERROR("Error connecting to cgroup manager");
+ return false;
+ }
if ( cgmanager_create_sync(NULL, cgroup_manager, controller,
cgroup_path, existed) != 0) {
NihError *nerr;
ERROR("call to cgmanager_create_sync failed: %s", nerr->message);
nih_free(nerr);
ERROR("Failed to create %s:%s", controller, cgroup_path);
- return false;
+ ret = false;
}
- return true;
+ if (!cgm_keep_connection)
+ cgm_dbus_disconnect();
+ return ret;
}
static bool lxc_cgmanager_escape(void)
{
+ bool ret = true;
pid_t me = getpid();
int i;
+
+ if (!cgm_dbus_connect()) {
+ ERROR("Error connecting to cgroup manager");
+ return false;
+ }
for (i = 0; i < nr_subsystems; i++) {
if (cgmanager_move_pid_abs_sync(NULL, cgroup_manager,
subsystems[i], "/", me) != 0) {
ERROR("call to cgmanager_move_pid_abs_sync(%s) failed: %s",
subsystems[i], nerr->message);
nih_free(nerr);
- return false;
+ ret = false;
+ break;
}
}
- return true;
+ if (!cgm_keep_connection)
+ cgm_dbus_disconnect();
+ return ret;
}
struct chown_data {
return do_chown_cgroup(arg->controller, arg->cgroup_path, arg->origuid);
}
-/*
- * Called during container startup. The cgmanager socket is open
- */
+/* Internal helper. Must be called with the cgmanager dbus socket open */
static bool lxc_cgmanager_chmod(const char *controller,
const char *cgroup_path, const char *file, int mode)
{
return true;
}
-/*
- * Called during container startup. The cgmanager socket is open
- */
+/* Internal helper. Must be called with the cgmanager dbus socket open */
static bool chown_cgroup(const char *controller, const char *cgroup_path,
struct lxc_conf *conf)
{
return false;
if (!lxc_cgmanager_chmod(controller, cgroup_path, "cgroup.procs", 0775))
return false;
+
return true;
}
#define CG_REMOVE_RECURSIVE 1
+/* Internal helper. Must be called with the cgmanager dbus socket open */
static void cgm_remove_cgroup(const char *controller, const char *path)
{
int existed;
}
/*
- * We are starting up a container. We open the cgmanager socket, and leave it
- * open (and mutex held) for the rest of container startup.
+ * We are starting up a container. We open the cgmanager socket, and set
+ * cgm_keep_connection to true so that helpers will keep the connection
+ * open.
*/
static void *cgm_init(const char *name)
{
return NULL;
}
d = malloc(sizeof(*d));
- if (!d)
+ if (!d) {
+ cgm_dbus_disconnect();
return NULL;
+ }
memset(d, 0, sizeof(*d));
d->name = strdup(name);
- if (!d->name)
+ if (!d->name) {
+ cgm_dbus_disconnect();
goto err1;
+ }
+ cgm_keep_connection = true;
/* if we are running as root, use system cgroup pattern, otherwise
* just create a cgroup under the current one. But also fall back to
return NULL;
}
-/*
- * Called after a failed container startup. The cgmanager socket was just
- * closed at end of lxc_spawn. We need to re-open
- */
+/* Called after a failed container startup */
static void cgm_destroy(void *hdata)
{
struct cgm_data *d = hdata;
if (d->cgroup_path)
free(d->cgroup_path);
free(d);
- cgm_dbus_disconnect();
+ if (!cgm_keep_connection)
+ cgm_dbus_disconnect();
}
/*
* remove all the cgroups created
+ * called internally with dbus connection open
*/
static inline void cleanup_cgroups(char *path)
{
cgm_remove_cgroup(subsystems[i], path);
}
-/*
- * Called during container startup. The cgmanager socket is open
- */
static inline bool cgm_create(void *hdata)
{
struct cgm_data *d = hdata;
// XXX we should send a hint to the cgmanager that when these
// cgroups become empty they should be deleted. Requires a cgmanager
// extension
+ if (!cgm_dbus_connect()) {
+ ERROR("Error connecting to cgroup manager");
+ return false;
+ }
memset(result, 0, MAXPATHLEN);
tmp = lxc_string_replace("%n", d->name, d->cgroup_pattern);
if (!tmp)
- return false;
+ goto bad;
if (strlen(tmp) >= MAXPATHLEN) {
free(tmp);
- return false;
+ goto bad;
}
strcpy(result, tmp);
baselen = strlen(result);
again:
if (index == 100) { // turn this into a warn later
ERROR("cgroup error? 100 cgroups with this name already running");
- return false;
+ goto bad;
}
if (index) {
ret = snprintf(result+baselen, MAXPATHLEN-baselen, "-%d", index);
if (ret < 0 || ret >= MAXPATHLEN-baselen)
- return false;
+ goto bad;
}
existed = 0;
for (i = 0; i < nr_subsystems; i++) {
if (!lxc_cgmanager_create(subsystems[i], tmp, &existed)) {
ERROR("Error creating cgroup %s:%s", subsystems[i], result);
cleanup_cgroups(tmp);
- return false;
+ goto bad;
}
if (existed == 1)
goto next;
cgroup_path = strdup(tmp);
if (!cgroup_path) {
cleanup_cgroups(tmp);
- return false;
+ goto bad;
}
d->cgroup_path = cgroup_path;
+ if (!cgm_keep_connection)
+ cgm_dbus_disconnect();
return true;
next:
cleanup_cgroups(tmp);
index++;
goto again;
+bad:
+ if (!cgm_keep_connection)
+ cgm_dbus_disconnect();
+ return false;
}
/*
* hierarchy.
* All the subsystems in this hierarchy are co-mounted, so we only
* need to transition the task into one of the cgroups
+ *
+ * Internal helper, must be called with cgmanager dbus socket open
*/
static bool lxc_cgmanager_enter(pid_t pid, const char *controller,
const char *cgroup_path)
return true;
}
+/* Internal helper, must be called with cgmanager dbus socket open */
static bool do_cgm_enter(pid_t pid, const char *cgroup_path)
{
int i;
return true;
}
-/*
- * called during container startup. cgmanager socket is open
- */
static inline bool cgm_enter(void *hdata, pid_t pid)
{
struct cgm_data *d = hdata;
+ bool ret = false;
- if (!d || !d->cgroup_path)
+ if (!cgm_dbus_connect()) {
+ ERROR("Error connecting to cgroup manager");
return false;
- return do_cgm_enter(pid, d->cgroup_path);
+ }
+ if (!d || !d->cgroup_path)
+ goto out;
+ if (do_cgm_enter(pid, d->cgroup_path))
+ ret = true;
+out:
+ if (!cgm_keep_connection)
+ cgm_dbus_disconnect();
+ return ret;
}
static const char *cgm_get_cgroup(void *hdata, const char *subsystem)
* nrtasks is called by the utmp helper by the container monitor.
* cgmanager socket was closed after cgroup setup was complete, so we need
* to reopen here.
+ *
+ * Return -1 on error.
*/
static int cgm_get_nrtasks(void *hdata)
{
size_t pids_len;
if (!d || !d->cgroup_path)
- return false;
+ return -1;
if (!cgm_dbus_connect()) {
ERROR("Error connecting to cgroup manager");
- return false;
+ return -1;
}
if (cgmanager_get_tasks_sync(NULL, cgroup_manager, subsystems[0],
d->cgroup_path, &pids, &pids_len) != 0) {
nerr = nih_error_get();
ERROR("call to cgmanager_get_tasks_sync failed: %s", nerr->message);
nih_free(nerr);
- cgm_dbus_disconnect();
- return -1;
+ pids_len = -1;
+ goto out;
}
nih_free(pids);
- cgm_dbus_disconnect();
+out:
+ if (!cgm_keep_connection)
+ cgm_dbus_disconnect();
return pids_len;
}
-/*
- * cgm_get is called to get container cgroup settings. cgmanager is not
- * connected.
- */
+/* cgm_get is called to get container cgroup settings, not during startup */
static int cgm_get(const char *filename, char *value, size_t len, const char *name, const char *lxcpath)
{
char *result, *controller, *key, *cgroup;
return -1;
if (!cgm_dbus_connect()) {
ERROR("Error connecting to cgroup manager");
- free(cgroup);
return -1;
}
if (cgmanager_get_value_sync(NULL, cgroup_manager, controller, cgroup, filename, &result) != 0) {
nerr = nih_error_get();
nih_free(nerr);
free(cgroup);
- cgm_dbus_disconnect();
+ if (!cgm_keep_connection)
+ cgm_dbus_disconnect();
return -1;
}
- cgm_dbus_disconnect();
+ if (!cgm_keep_connection)
+ cgm_dbus_disconnect();
free(cgroup);
newlen = strlen(result);
if (!value) {
return newlen;
}
+/* internal helper - call with cgmanager dbus connection open */
static int cgm_do_set(const char *controller, const char *file,
const char *cgroup, const char *value)
{
return ret;
}
-/*
- * cgm_set is called to change cgroup settings. cgmanager is not connected
- */
+/* cgm_set is called to change cgroup settings, not during startup */
static int cgm_set(const char *filename, const char *value, const char *name, const char *lxcpath)
{
char *controller, *key, *cgroup;
if (!cgm_dbus_connect()) {
ERROR("Error connecting to cgroup manager");
free(cgroup);
- return -1;
+ return false;
}
ret = cgm_do_set(controller, filename, cgroup, value);
- cgm_dbus_disconnect();
+ if (!cgm_keep_connection)
+ cgm_dbus_disconnect();
free(cgroup);
return ret;
}
{
int i;
- lock_mutex(&thread_mutex);
for (i = 0; i < nr_subsystems; i++)
free(subsystems[i]);
free(subsystems);
subsystems = NULL;
nr_subsystems = 0;
- unlock_mutex(&thread_mutex);
}
static bool collect_subsytems(void)
if (subsystems) // already initialized
return true;
- lock_mutex(&thread_mutex);
f = fopen_cloexec("/proc/cgroups", "r");
if (!f) {
- unlock_mutex(&thread_mutex);
return false;
}
while (getline(&line, &sz, f) != -1) {
}
fclose(f);
- unlock_mutex(&thread_mutex);
-
if (!nr_subsystems) {
ERROR("No cgroup subsystems found");
return false;
out_free:
fclose(f);
- unlock_mutex(&thread_mutex);
free_subsystems();
return false;
}
goto err1;
// root; try to escape to root cgroup
- if (geteuid() == 0 && !lxc_cgmanager_escape()) {
- cgm_dbus_disconnect();
+ if (geteuid() == 0 && !lxc_cgmanager_escape())
goto err2;
- }
cgm_dbus_disconnect();
return &cgmanager_ops;
return NULL;
}
-/*
- * unfreeze is called by the command api after killing a container.
- * cgmanager is not connected.
- */
+/* unfreeze is called by the command api after killing a container. */
static bool cgm_unfreeze(void *hdata)
{
struct cgm_data *d = hdata;
+ bool ret = true;
if (!d || !d->cgroup_path)
return false;
ERROR("call to cgmanager_set_value_sync failed: %s", nerr->message);
nih_free(nerr);
ERROR("Error unfreezing %s", d->cgroup_path);
- cgm_dbus_disconnect();
- return false;
+ ret = false;
}
- cgm_dbus_disconnect();
- return true;
+ if (!cgm_keep_connection)
+ cgm_dbus_disconnect();
+ return ret;
}
-/*
- * setup_limits is called during startup. cgmanager is connected, and mutex
- * is held
- */
static bool cgm_setup_limits(void *hdata, struct lxc_list *cgroup_settings, bool do_devices)
{
struct cgm_data *d = hdata;
if (!d || !d->cgroup_path)
return false;
+ if (!cgm_dbus_connect()) {
+ ERROR("Error connecting to cgroup manager");
+ return false;
+ }
+
lxc_list_for_each(iterator, cgroup_settings) {
char controller[100], *p;
cg = iterator->elem;
ret = true;
INFO("cgroup limits have been setup");
out:
+ if (!cgm_keep_connection)
+ cgm_dbus_disconnect();
return ret;
}
if (!d || !d->cgroup_path)
return false;
+ if (!cgm_dbus_connect()) {
+ ERROR("Error connecting to cgroup manager");
+ return false;
+ }
for (i = 0; i < nr_subsystems; i++) {
if (!chown_cgroup(subsystems[i], d->cgroup_path, conf))
WARN("Failed to chown %s:%s to container root",
subsystems[i], d->cgroup_path);
}
+ if (!cgm_keep_connection)
+ cgm_dbus_disconnect();
return true;
}
ERROR("Could not load container %s:%s", lxcpath, name);
return false;
}
- if (!collect_subsytems()) {
- ERROR("Error collecting cgroup subsystems");
- goto out;
+ if (!cgm_dbus_connect()) {
+ ERROR("Error connecting to cgroup manager");
+ return false;
}
// cgm_create makes sure that we have the same cgroup name for all
// subsystems, so since this is a slow command over the cmd socket,
}
if (!(pass = do_cgm_enter(pid, cgroup)))
ERROR("Failed to enter group %s", cgroup);
- cgm_dbus_disconnect();
out:
free(cgroup);
lxc_container_put(c);
+ if (!cgm_keep_connection)
+ cgm_dbus_disconnect();
return pass;
}