[lxc-devel] [PATCH] cgmanager: implement attach

Stéphane Graber stgraber at ubuntu.com
Fri Jan 31 09:42:58 UTC 2014


On Thu, Jan 30, 2014 at 12:15:32PM +0000, Serge Hallyn wrote:
> The cgroupfs-specific code is moved from attach.c to cgroup.c.
> 
> lxc-cgmanager now only chgrps the container's cgroup, so that the
> unprivileged user still owns the tasks file allowing him to enter
> the container cgroup (for attach).
> 
> Some other changes rolled into the cgmanager update:
> 
> Make the list of subsystems not per-handler, as it will not change.  As
> a result, the only state we need to keep in the per-handler cgroup data
> is the char *cgroup_path, so we can drop the cgm_data struct altogether.
> 
> Catch nih errors (as not doing so causes later crashes).
> 
> Signed-off-by: Serge Hallyn <serge.hallyn at ubuntu.com>

Acked-by: Stéphane Graber <stgraber at ubuntu.com>

> ---
>  src/lxc/attach.c    |  26 +----
>  src/lxc/cgmanager.c | 310 ++++++++++++++++++++++++++++++++++------------------
>  src/lxc/cgroup.c    |  35 ++++++
>  src/lxc/cgroup.h    |   2 +
>  src/lxc/utils.c     |  25 +++++
>  src/lxc/utils.h     |   5 +
>  6 files changed, 271 insertions(+), 132 deletions(-)
> 
> diff --git a/src/lxc/attach.c b/src/lxc/attach.c
> index 3ee1d4d..8b9bc8d 100644
> --- a/src/lxc/attach.c
> +++ b/src/lxc/attach.c
> @@ -698,32 +698,8 @@ int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_fun
>  
>  		/* attach to cgroup, if requested */
>  		if (options->attach_flags & LXC_ATTACH_MOVE_TO_CGROUP) {
> -			struct cgroup_meta_data *meta_data;
> -			struct cgroup_process_info *container_info;
> -
> -			meta_data = lxc_cgroup_load_meta();
> -			if (!meta_data) {
> -				ERROR("could not move attached process %ld to cgroup of container", (long)pid);
> +			if (!lxc_cgroup_attach(name, lxcpath, pid))
>  				goto cleanup_error;
> -			}
> -
> -			container_info = lxc_cgroup_get_container_info(name, lxcpath, meta_data);
> -			lxc_cgroup_put_meta(meta_data);
> -			if (!container_info) {
> -				ERROR("could not move attached process %ld to cgroup of container", (long)pid);
> -				goto cleanup_error;
> -			}
> -
> -			/*
> -			 * TODO - switch over to using a cgroup_operation.  We can't use
> -			 * cgroup_enter() as that takes a handler.
> -			 */
> -			ret = lxc_cgroupfs_enter(container_info, pid, false);
> -			lxc_cgroup_process_info_free(container_info);
> -			if (ret < 0) {
> -				ERROR("could not move attached process %ld to cgroup of container", (long)pid);
> -				goto cleanup_error;
> -			}
>  		}
>  
>  		/* Let the child process know to go ahead */
> diff --git a/src/lxc/cgmanager.c b/src/lxc/cgmanager.c
> index bd3d7ff..2097a25 100644
> --- a/src/lxc/cgmanager.c
> +++ b/src/lxc/cgmanager.c
> @@ -55,12 +55,15 @@ lxc_log_define(lxc_cgmanager, lxc);
>  #include <nih-dbus/dbus_connection.h>
>  #include <cgmanager-client/cgmanager-client.h>
>  #include <nih/alloc.h>
> +#include <nih/error.h>
>  NihDBusProxy *cgroup_manager = NULL;
>  
>  extern struct cgroup_ops *active_cg_ops;
>  bool cgmanager_initialized = false;
>  bool use_cgmanager = true;
>  static struct cgroup_ops cgmanager_ops;
> +static int nr_subsystems;
> +static char **subsystems;
>  
>  bool lxc_init_cgmanager(void);
>  static void cgmanager_disconnected(DBusConnection *connection)
> @@ -105,10 +108,8 @@ static int send_creds(int sock, int rpid, int ruid, int rgid)
>  	msg.msg_iov = &iov;
>  	msg.msg_iovlen = 1;
>  
> -	if (sendmsg(sock, &msg, 0) < 0) {
> -		perror("sendmsg");
> +	if (sendmsg(sock, &msg, 0) < 0)
>  		return -1;
> -	}
>  	return 0;
>  }
>  
> @@ -121,7 +122,12 @@ bool lxc_init_cgmanager(void)
>  
>  	connection = nih_dbus_connect(CGMANAGER_DBUS_SOCK, cgmanager_disconnected);
>  	if (!connection) {
> -		ERROR("Error opening cgmanager connection at %s", CGMANAGER_DBUS_SOCK);
> +		NihError *nerr;
> +		nerr = nih_error_get();
> +		ERROR("Error opening cgmanager connection at %s: %s", CGMANAGER_DBUS_SOCK,
> +			nerr->message);
> +		nih_free(nerr);
> +		dbus_error_free(&dbus_error);
>  		return false;
>  	}
>  	dbus_connection_set_exit_on_disconnect(connection, FALSE);
> @@ -131,28 +137,31 @@ bool lxc_init_cgmanager(void)
>  				"/org/linuxcontainers/cgmanager", NULL, NULL);
>  	dbus_connection_unref(connection);
>  	if (!cgroup_manager) {
> +		NihError *nerr;
> +		nerr = nih_error_get();
> +		ERROR("Error opening cgmanager proxy: %s", nerr->message);
> +		nih_free(nerr);
>  		return false;
>  	}
>  	active_cg_ops = &cgmanager_ops;
> +	// force fd passing negotiation
> +	if (cgmanager_ping_sync(NULL, cgroup_manager, 0) != 0) {
> +		NihError *nerr;
> +		nerr = nih_error_get();
> +		ERROR("Error pinging cgroup manager: %s", nerr->message);
> +		nih_free(nerr);
> +	}
>  	return true;
>  }
>  
> -/*
> - * Use the cgmanager to move a task into a cgroup for a particular
> - * hierarchy.
> - * All the subsystems in this hierarchy are co-mounted, so we only
> - * need to transition the task into one of the cgroups
> - */
> -static bool lxc_cgmanager_enter(pid_t pid, char *controller, char *cgroup_path)
> -{
> -	return cgmanager_move_pid_sync(NULL, cgroup_manager, controller,
> -				       cgroup_path, pid) == 0;
> -}
> -
>  static bool lxc_cgmanager_create(const char *controller, const char *cgroup_path, int32_t *existed)
>  {
>  	if ( cgmanager_create_sync(NULL, cgroup_manager, controller,
>  				       cgroup_path, existed) != 0) {
> +		NihError *nerr;
> +		nerr = nih_error_get();
> +		ERROR("call to cgmanager_create_sync failed: %s", nerr->message);
> +		nih_free(nerr);
>  		ERROR("Failed to create %s:%s", controller, cgroup_path);
>  		return false;
>  	}
> @@ -163,17 +172,16 @@ static bool lxc_cgmanager_create(const char *controller, const char *cgroup_path
>  struct chown_data {
>  	const char *controller;
>  	const char *cgroup_path;
> +	uid_t origuid;
>  };
>  
> -static int do_chown_cgroup(const char *controller, const char *cgroup_path)
> +static int do_chown_cgroup(const char *controller, const char *cgroup_path,
> +		uid_t origuid)
>  {
>  	int sv[2] = {-1, -1}, optval = 1, ret = -1;
>  	char buf[1];
>  
> -	if (setgid(0) < 0)
> -		WARN("Failed to setgid to 0");
> -	if (setuid(0) < 0)
> -		WARN("Failed to setuid to 0");
> +	uid_t caller_nsuid = get_ns_uid(origuid);
>  
>  	if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sv) < 0) {
>  		SYSERROR("Error creating socketpair");
> @@ -189,7 +197,10 @@ static int do_chown_cgroup(const char *controller, const char *cgroup_path)
>  	}
>  	if ( cgmanager_chown_scm_sync(NULL, cgroup_manager, controller,
>  				       cgroup_path, sv[1]) != 0) {
> -		ERROR("call to cgmanager_chown_scm_sync failed");
> +		NihError *nerr;
> +		nerr = nih_error_get();
> +		ERROR("call to cgmanager_chown_scm_sync failed: %s", nerr->message);
> +		nih_free(nerr);
>  		goto out;
>  	}
>  	/* now send credentials */
> @@ -206,7 +217,7 @@ static int do_chown_cgroup(const char *controller, const char *cgroup_path)
>  		goto out;
>  	}
>  	if (send_creds(sv[0], getpid(), getuid(), getgid())) {
> -		ERROR("Error sending pid over SCM_CREDENTIAL");
> +		SYSERROR("%s: Error sending pid over SCM_CREDENTIAL", __func__);
>  		goto out;
>  	}
>  	FD_ZERO(&rfds);
> @@ -219,8 +230,8 @@ static int do_chown_cgroup(const char *controller, const char *cgroup_path)
>  		ERROR("Error getting reply from server over socketpair");
>  		goto out;
>  	}
> -	if (send_creds(sv[0], getpid(), 0, 0)) {
> -		ERROR("Error sending pid over SCM_CREDENTIAL");
> +	if (send_creds(sv[0], getpid(), caller_nsuid, 0)) {
> +		SYSERROR("%s: Error sending pid over SCM_CREDENTIAL", __func__);
>  		goto out;
>  	}
>  	FD_ZERO(&rfds);
> @@ -241,21 +252,26 @@ out:
>  static int chown_cgroup_wrapper(void *data)
>  {
>  	struct chown_data *arg = data;
> -	return do_chown_cgroup(arg->controller, arg->cgroup_path);
> +
> +	if (setresgid(0,0,0) < 0)
> +		SYSERROR("Failed to setgid to 0");
> +	if (setresuid(0,0,0) < 0)
> +		SYSERROR("Failed to setuid to 0");
> +	return do_chown_cgroup(arg->controller, arg->cgroup_path, arg->origuid);
>  }
>  
>  static bool chown_cgroup(const char *controller, const char *cgroup_path,
>  			struct lxc_conf *conf)
>  {
>  	struct chown_data data;
> -	data.controller = controller;
> -	data.cgroup_path = cgroup_path;
>  
> -	if (lxc_list_empty(&conf->id_map)) {
> -		if (do_chown_cgroup(controller, cgroup_path) < 0)
> -			return false;
> +	if (lxc_list_empty(&conf->id_map))
> +		/* If there's no mapping then we don't need to chown */
>  		return true;
> -	}
> +
> +	data.controller = controller;
> +	data.cgroup_path = cgroup_path;
> +	data.origuid = geteuid();
>  
>  	if (userns_exec_1(conf, chown_cgroup_wrapper, &data) < 0) {
>  		ERROR("Error requesting cgroup chown in new namespace");
> @@ -264,50 +280,45 @@ static bool chown_cgroup(const char *controller, const char *cgroup_path,
>  	return true;
>  }
>  
> -struct cgm_data {
> -	int nr_subsystems;
> -	char **subsystems;
> -	char *cgroup_path;
> -};
> -
>  #define CG_REMOVE_RECURSIVE 1
> -void cgmanager_remove_cgroup(const char *controller, const char *path)
> +static void cgm_remove_cgroup(const char *controller, const char *path)
>  {
>  	int existed;
>  	if ( cgmanager_remove_sync(NULL, cgroup_manager, controller,
> -				   path, CG_REMOVE_RECURSIVE, &existed) != 0)
> +				   path, CG_REMOVE_RECURSIVE, &existed) != 0) {
> +		NihError *nerr;
> +		nerr = nih_error_get();
> +		ERROR("call to cgmanager_remove_sync failed: %s", nerr->message);
> +		nih_free(nerr);
>  		ERROR("Error removing %s:%s", controller, path);
> +	}
>  	if (existed == -1)
>  		INFO("cgroup removal attempt: %s:%s did not exist", controller, path);
>  }
>  
>  static void cgm_destroy(struct lxc_handler *handler)
>  {
> -	struct cgm_data *d = handler->cgroup_info->data;
> +	char *cgroup_path = handler->cgroup_info->data;
>  	int i;
>  
> -	if (!d)
> +	if (!cgroup_path)
>  		return;
> -	for (i=0; i<d->nr_subsystems; i++) {
> -		if (d->cgroup_path)
> -			cgmanager_remove_cgroup(d->subsystems[i], d->cgroup_path);
> -		free(d->subsystems[i]);
> -	}
> -	free(d->subsystems);
> -	free(d->cgroup_path);
> -	free(d);
> +
> +	for (i = 0; i < nr_subsystems; i++)
> +		cgm_remove_cgroup(subsystems[i], cgroup_path);
> +
> +	free(cgroup_path);
>  	handler->cgroup_info->data = NULL;
>  }
>  
>  /*
>   * remove all the cgroups created
>   */
> -static inline void cleanup_cgroups(struct cgm_data *d, char *path)
> +static inline void cleanup_cgroups(char *path)
>  {
>  	int i;
> -	for (i = 0; i < d->nr_subsystems; i++) {
> -		cgmanager_remove_cgroup(d->subsystems[i], path);
> -	}
> +	for (i = 0; i < nr_subsystems; i++)
> +		cgm_remove_cgroup(subsystems[i], path);
>  }
>  
>  static inline bool cgm_create(struct lxc_handler *handler)
> @@ -315,7 +326,7 @@ static inline bool cgm_create(struct lxc_handler *handler)
>  	int i, index=0, baselen, ret;
>  	int32_t existed;
>  	char result[MAXPATHLEN], *tmp;
> -	struct cgm_data *d = handler->cgroup_info->data;
> +	char *cgroup_path = handler->cgroup_info->data;
>  
>  // XXX we should send a hint to the cgmanager that when these
>  // cgroups become empty they should be deleted.  Requires a cgmanager
> @@ -344,44 +355,70 @@ again:
>  			return false;
>  	}
>  	existed = 0;
> -	for (i = 0; i < d->nr_subsystems; i++) {
> -		if (!lxc_cgmanager_create(d->subsystems[i], tmp, &existed)) {
> -			ERROR("Error creating cgroup %s:%s", d->subsystems[i], result);
> -			cleanup_cgroups(d, tmp);
> +	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;
>  		}
>  		if (existed == 1)
>  			goto next;
>  	}
>  	// success
> -	d->cgroup_path = strdup(tmp);
> -	if (!d->cgroup_path) {
> -		cleanup_cgroups(d, tmp);
> +	cgroup_path = strdup(tmp);
> +	if (!cgroup_path) {
> +		cleanup_cgroups(tmp);
>  		return false;
>  	}
> +	handler->cgroup_info->data = cgroup_path;
>  	return true;
>  next:
> -	cleanup_cgroups(d, tmp);
> +	cleanup_cgroups(tmp);
>  	index++;
>  	goto again;
>  }
>  
> -static inline bool cgm_enter(struct lxc_handler *handler)
> +/*
> + * Use the cgmanager to move a task into a cgroup for a particular
> + * hierarchy.
> + * All the subsystems in this hierarchy are co-mounted, so we only
> + * need to transition the task into one of the cgroups
> + */
> +static bool lxc_cgmanager_enter(pid_t pid, const char *controller,
> +		const char *cgroup_path)
> +{
> +	if (cgmanager_move_pid_sync(NULL, cgroup_manager, controller,
> +			cgroup_path, pid) != 0) {
> +		NihError *nerr;
> +		nerr = nih_error_get();
> +		ERROR("call to cgmanager_move_pid_sync failed: %s", nerr->message);
> +		nih_free(nerr);
> +		return false;
> +	}
> +	return true;
> +}
> +
> +static bool do_cgm_enter(pid_t pid, const char *cgroup_path)
>  {
> -	struct cgm_data *d = handler->cgroup_info->data;
>  	int i;
>  
> -	for (i = 0; i < d->nr_subsystems; i++) {
> -		if (!lxc_cgmanager_enter(handler->pid, d->subsystems[i], d->cgroup_path))
> +	for (i = 0; i < nr_subsystems; i++) {
> +		if (!lxc_cgmanager_enter(pid, subsystems[i], cgroup_path))
>  			return false;
>  	}
>  	return true;
>  }
>  
> +static inline bool cgm_enter(struct lxc_handler *handler)
> +{
> +	char *cgroup_path = handler->cgroup_info->data;
> +	return do_cgm_enter(handler->pid, cgroup_path);
> +}
> +
>  static char *cgm_get_cgroup(struct lxc_handler *handler, const char *subsystem)
>  {
> -	struct cgm_data *d = handler->cgroup_info->data;
> -	return d->cgroup_path;
> +	char *cgroup_path = handler->cgroup_info->data;
> +	return cgroup_path;
>  }
>  
>  int cgm_get(const char *filename, char *value, size_t len, const char *name, const char *lxcpath)
> @@ -401,8 +438,14 @@ int cgm_get(const char *filename, char *value, size_t len, const char *name, con
>  	if (!cgroup)
>  		return -1;
>  	if (cgmanager_get_value_sync(NULL, cgroup_manager, controller, cgroup, filename, &result) != 0) {
> -		ERROR("Error getting value for %s from cgmanager for cgroup %s (%s:%s)",
> -			filename, cgroup, lxcpath, name);
> +		/*
> +		 * must consume the nih error
> +		 * However don't print out an error as the key may simply not exist
> +		 * on the host
> +		 */
> +		NihError *nerr;
> +		nerr = nih_error_get();
> +		nih_free(nerr);
>  		free(cgroup);
>  		return -1;
>  	}
> @@ -433,8 +476,13 @@ static int cgm_do_set(const char *controller, const char *file,
>  	int ret;
>  	ret = cgmanager_set_value_sync(NULL, cgroup_manager, controller,
>  				 cgroup, file, value);
> -	if (ret != 0)
> +	if (ret != 0) {
> +		NihError *nerr;
> +		nerr = nih_error_get();
> +		ERROR("call to cgmanager_remove_sync failed: %s", nerr->message);
> +		nih_free(nerr);
>  		ERROR("Error setting cgroup %s limit %s", file, cgroup);
> +	}
>  	return ret;
>  }
>  
> @@ -462,26 +510,18 @@ int cgm_set(const char *filename, const char *value, const char *name, const cha
>  	return ret;
>  }
>  
> -/*
> - * TODO really this should be done once for global data, not once
> - * per container
> - */
> -static inline bool cgm_init(struct lxc_handler *handler)
> +static bool collect_subsytems(void)
>  {
> -	struct cgm_data *d = malloc(sizeof(*d));
>  	char *line = NULL, *tab1;
>  	size_t sz = 0, i;
>  	FILE *f;
>  
> -	if (!d)
> -		return false;
> -	d->nr_subsystems = 0;
> -	d->subsystems = NULL;
> +	if (subsystems) // already initialized
> +		return true;
> +
>  	f = fopen_cloexec("/proc/cgroups", "r");
> -	if (!f) {
> -		free(d);
> +	if (!f)
>  		return false;
> -	}
>  	while (getline(&line, &sz, f) != -1) {
>  		char **tmp;
>  		if (line[0] == '#')
> @@ -492,37 +532,51 @@ static inline bool cgm_init(struct lxc_handler *handler)
>  		if (!tab1)
>  			continue;
>  		*tab1 = '\0';
> -		tmp = realloc(d->subsystems, (d->nr_subsystems+1)*sizeof(char *));
> -		if (!tmp) {
> +		tmp = realloc(subsystems, (nr_subsystems+1)*sizeof(char *));
> +		if (!tmp)
>  			goto out_free;
> -		}
> -		d->subsystems = tmp;
> -		d->subsystems[d->nr_subsystems] = strdup(line);
> -		if (!d->subsystems[d->nr_subsystems])
> +
> +		subsystems = tmp;
> +		tmp[nr_subsystems] = strdup(line);
> +		if (!tmp[nr_subsystems])
>  			goto out_free;
> -		d->nr_subsystems++;
> +		nr_subsystems++;
>  	}
>  	fclose(f);
>  
> -	d->cgroup_path = NULL;
> -	handler->cgroup_info->data = d;
> +	if (!nr_subsystems) {
> +		ERROR("No cgroup subsystems found");
> +		return false;
> +	}
> +
>  	return true;
>  
>  out_free:
> -	for (i=0; i<d->nr_subsystems; i++)
> -		free(d->subsystems[i]);
> -	free(d->subsystems);
> -	free(d);
> +	fclose(f);
> +	for (i = 0; i < nr_subsystems; i++)
> +		free(subsystems[i]);
> +	free(subsystems);
> +	subsystems = NULL;
> +	nr_subsystems = 0;
>  	return false;
>  }
>  
> +static inline bool cgm_init(struct lxc_handler *handler)
> +{
> +	return collect_subsytems();
> +}
> +
>  static int cgm_unfreeze_fromhandler(struct lxc_handler *handler)
>  {
> -	struct cgm_data *d = handler->cgroup_info->data;
> +	char *cgroup_path = handler->cgroup_info->data;
>  
> -	if (cgmanager_set_value_sync(NULL, cgroup_manager, "freezer", d->cgroup_path,
> +	if (cgmanager_set_value_sync(NULL, cgroup_manager, "freezer", cgroup_path,
>  			"freezer.state", "THAWED") != 0) {
> -		ERROR("Error unfreezing %s", d->cgroup_path);
> +		NihError *nerr;
> +		nerr = nih_error_get();
> +		ERROR("call to cgmanager_set_value_sync failed: %s", nerr->message);
> +		nih_free(nerr);
> +		ERROR("Error unfreezing %s", cgroup_path);
>  		return -1;
>  	}
>  	return 0;
> @@ -534,7 +588,7 @@ static bool setup_limits(struct lxc_handler *h, bool do_devices)
>  	struct lxc_cgroup *cg;
>  	bool ret = false;
>  	struct lxc_list *cgroup_settings = &h->conf->cgroup;
> -	struct cgm_data *d = h->cgroup_info->data;
> +	char *cgroup_path = h->cgroup_info->data;
>  
>  	if (lxc_list_empty(cgroup_settings))
>  		return true;
> @@ -550,7 +604,7 @@ static bool setup_limits(struct lxc_handler *h, bool do_devices)
>  		p = strchr(controller, '.');
>  		if (p)
>  			*p = '\0';
> -		if (cgm_do_set(controller, cg->subsystem, d->cgroup_path
> +		if (cgm_do_set(controller, cg->subsystem, cgroup_path
>  				, cg->value) < 0) {
>  			ERROR("Error setting %s to %s for %s\n",
>  			      cg->subsystem, cg->value, h->name);
> @@ -573,17 +627,58 @@ static bool cgm_setup_limits(struct lxc_handler *handler, bool with_devices)
>  
>  static bool cgm_chown(struct lxc_handler *handler)
>  {
> -	struct cgm_data *d = handler->cgroup_info->data;
> +	char *cgroup_path = handler->cgroup_info->data;
>  	int i;
>  
> -	for (i = 0; i < d->nr_subsystems; i++) {
> -		if (!chown_cgroup(d->subsystems[i], d->cgroup_path, handler->conf))
> +	for (i = 0; i < nr_subsystems; i++) {
> +		if (!chown_cgroup(subsystems[i], cgroup_path, handler->conf))
>  			WARN("Failed to chown %s:%s to container root",
> -				d->subsystems[i], d->cgroup_path);
> +				subsystems[i], cgroup_path);
>  	}
>  	return true;
>  }
>  
> +/*
> + * TODO: this should be re-written to use the get_config_item("lxc.id_map")
> + * cmd api instead of getting the idmap from c->lxc_conf.  The reason is
> + * that the id_maps may be different if the container was started with a
> + * -f or -s argument.
> + * The reason I'm punting on that is because we'll need to parse the
> + * idmap results.
> + */
> +static bool cgm_attach(const char *name, const char *lxcpath, pid_t pid)
> +{
> +	bool pass = false;
> +	char *cgroup = NULL;
> +	struct lxc_container *c;
> +
> +	c = lxc_container_new(name, lxcpath);
> +	if (!c) {
> +		ERROR("Could not load container %s:%s", lxcpath, name);
> +		return false;
> +	}
> +	if (!collect_subsytems()) {
> +		ERROR("Error collecting cgroup subsystems");
> +		goto out;
> +	}
> +	// 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,
> +	// just get the cgroup name for the first one.
> +	cgroup = lxc_cmd_get_cgroup_path(name, lxcpath, subsystems[0]);
> +	if (!cgroup) {
> +		ERROR("Failed to get cgroup for controller %s", subsystems[0]);
> +		goto out;
> +	}
> +
> +	if (!(pass = do_cgm_enter(pid, cgroup)))
> +		ERROR("Failed to enter group %s", cgroup);
> +
> +out:
> +	free(cgroup);
> +	lxc_container_put(c);
> +	return pass;
> +}
> +
>  static struct cgroup_ops cgmanager_ops = {
>  	.destroy = cgm_destroy,
>  	.init = cgm_init,
> @@ -597,5 +692,6 @@ static struct cgroup_ops cgmanager_ops = {
>  	.setup_limits = cgm_setup_limits,
>  	.name = "cgmanager",
>  	.chown = cgm_chown,
> +	.attach = cgm_attach,
>  };
>  #endif
> diff --git a/src/lxc/cgroup.c b/src/lxc/cgroup.c
> index a44982a..7456413 100644
> --- a/src/lxc/cgroup.c
> +++ b/src/lxc/cgroup.c
> @@ -2303,6 +2303,34 @@ bool cgroupfs_setup_limits(struct lxc_handler *h, bool with_devices)
>  	return do_setup_cgroup_limits(h, &h->conf->cgroup, with_devices) == 0;
>  }
>  
> +bool lxc_cgroupfs_attach(const char *name, const char *lxcpath, pid_t pid)
> +{
> +	struct cgroup_meta_data *meta_data;
> +	struct cgroup_process_info *container_info;
> +	int ret;
> +
> +	meta_data = lxc_cgroup_load_meta();
> +	if (!meta_data) {
> +		ERROR("could not move attached process %d to cgroup of container", pid);
> +		return false;
> +	}
> +
> +	container_info = lxc_cgroup_get_container_info(name, lxcpath, meta_data);
> +	lxc_cgroup_put_meta(meta_data);
> +	if (!container_info) {
> +		ERROR("could not move attached process %d to cgroup of container", pid);
> +		return false;
> +	}
> +
> +	ret = lxc_cgroupfs_enter(container_info, pid, false);
> +	lxc_cgroup_process_info_free(container_info);
> +	if (ret < 0) {
> +		ERROR("could not move attached process %d to cgroup of container", pid);
> +		return false;
> +	}
> +	return true;
> +}
> +
>  static struct cgroup_ops cgfs_ops = {
>  	.destroy = cgfs_destroy,
>  	.init = cgfs_init,
> @@ -2315,6 +2343,7 @@ static struct cgroup_ops cgfs_ops = {
>  	.unfreeze_fromhandler = cgfs_unfreeze_fromhandler,
>  	.setup_limits = cgroupfs_setup_limits,
>  	.name = "cgroupfs",
> +	.attach = lxc_cgroupfs_attach,
>  	.chown = NULL,
>  };
>  static void init_cg_ops(void)
> @@ -2421,3 +2450,9 @@ bool cgroup_chown(struct lxc_handler *handler)
>  		return active_cg_ops->chown(handler);
>  	return true;
>  }
> +
> +bool lxc_cgroup_attach(const char *name, const char *lxcpath, pid_t pid)
> +{
> +	init_cg_ops();
> +	return active_cg_ops->attach(name, lxcpath, pid);
> +}
> diff --git a/src/lxc/cgroup.h b/src/lxc/cgroup.h
> index a2e7cd3..a172ffb 100644
> --- a/src/lxc/cgroup.h
> +++ b/src/lxc/cgroup.h
> @@ -182,6 +182,7 @@ struct cgroup_ops {
>  	int (*unfreeze_fromhandler)(struct lxc_handler *handler);
>  	bool (*setup_limits)(struct lxc_handler *handler, bool with_devices);
>  	bool (*chown)(struct lxc_handler *handler);
> +	bool (*attach)(const char *name, const char *lxcpath, pid_t pid);
>  	const char *name;
>  };
>  
> @@ -216,6 +217,7 @@ extern bool cgroup_enter(struct lxc_handler *handler);
>  extern void cgroup_cleanup(struct lxc_handler *handler);
>  extern bool cgroup_create_legacy(struct lxc_handler *handler);
>  extern char *cgroup_get_cgroup(struct lxc_handler *handler, const char *subsystem);
> +extern bool lxc_cgroup_attach(const char *name, const char *lxcpath, pid_t pid);
>  extern int lxc_cgroup_set(const char *filename, const char *value, const char *name, const char *lxcpath);
>  extern int lxc_cgroup_get(const char *filename, char *value, size_t len, const char *name, const char *lxcpath);
>  extern int lxc_unfreeze_fromhandler(struct lxc_handler *handler);
> diff --git a/src/lxc/utils.c b/src/lxc/utils.c
> index 2f9e08b..5ab4c77 100644
> --- a/src/lxc/utils.c
> +++ b/src/lxc/utils.c
> @@ -1111,3 +1111,28 @@ int randseed(bool srand_it)
>  
>  	return seed;
>  }
> +
> +uid_t get_ns_uid(uid_t orig)
> +{
> +	char *line = NULL;
> +	size_t sz = 0;
> +	uid_t nsid, hostid, range;
> +	FILE *f = fopen("/proc/self/uid_map", "r");
> +	if (!f)
> +		return 0;
> +
> +	while (getline(&line, &sz, f) != -1) {
> +		if (sscanf(line, "%u %u %u", &nsid, &hostid, &range) != 3)
> +			continue;
> +		if (hostid <= orig && hostid + range > orig) {
> +			nsid += orig - hostid;
> +			goto found;
> +		}
> +	}
> +
> +	nsid = 0;
> +found:
> +	fclose(f);
> +	free(line);
> +	return nsid;
> +}
> diff --git a/src/lxc/utils.h b/src/lxc/utils.h
> index e148fd8..f9037b7 100644
> --- a/src/lxc/utils.h
> +++ b/src/lxc/utils.h
> @@ -265,4 +265,9 @@ extern int randseed(bool);
>  inline static bool am_unpriv(void) {
>  	return geteuid() != 0;
>  }
> +
> +/*
> + * parse /proc/self/uid_map to find what @orig maps to
> + */
> +extern uid_t get_ns_uid(uid_t orig);
>  #endif
> -- 
> 1.8.5.3
> 
> _______________________________________________
> lxc-devel mailing list
> lxc-devel at lists.linuxcontainers.org
> http://lists.linuxcontainers.org/listinfo/lxc-devel

-- 
Stéphane Graber
Ubuntu developer
http://www.ubuntu.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20140131/a6544cba/attachment-0001.pgp>


More information about the lxc-devel mailing list