[cgmanager-devel] getTasks method

Serge Hallyn serge.hallyn at ubuntu.com
Wed Jan 15 23:34:50 UTC 2014


Quoting Stéphane Graber (stgraber at ubuntu.com):
> On Wed, Jan 15, 2014 at 03:21:28PM -0600, Serge Hallyn wrote:
> > One method which somehow never made it into my design is nrtasks, to
> > return the list of tasks in the container.
> > 
> > For the non-scm, same-pidns version, this could return a dbus array
> > of int32s.  For the scm version, I only see two possibilities.  The
> > potentially insecure one involves sending an fd for the open tasks
> > file over SCM.  The client can then read the file, which should have
> > pids translated to the client's pidns.
> 
> Are you sure this would work?

Not entirely, but I would have thought so.  In any case I pursued the
other track.  Here is the patch I have now.  Only compile-tested so far,
and I just about need to stop for the day:

>From fac3ff60f43ae431dba57ca44f50a9457e231877 Mon Sep 17 00:00:00 2001
From: Serge Hallyn <serge.hallyn at ubuntu.com>
Date: Wed, 15 Jan 2014 17:31:52 -0600
Subject: [PATCH 1/1] implement getTasks and getTasksScm

getTasks returns the tasks in a given cgroup as an array of ints.

getTasksScm sends the result over the passed-in unix fd.  It first
sends the # pids, then sends each pid as a separate SCM_CRED so as
to convert the pids to the client's pidns.
---
 access_checks.c                   |  99 ++++++++++++++++
 access_checks.h                   |   2 +
 cgmanager-proxy.c                 | 234 ++++++++++++++++++++++++++++++++------
 cgmanager.c                       | 145 +++++++++++++++++++++++
 configure.ac                      |   2 +
 fs.c                              |  41 +++++++
 fs.h                              |   1 +
 org.linuxcontainers.cgmanager.xml |  11 ++
 8 files changed, 503 insertions(+), 32 deletions(-)

diff --git a/access_checks.c b/access_checks.c
index 85545df..f6f7eea 100644
--- a/access_checks.c
+++ b/access_checks.c
@@ -78,6 +78,105 @@ bool get_nih_io_creds(NihIo *io, struct ucred *ucred)
 	return true;
 }
 
+int send_creds(int sock, struct ucred cred)
+{
+	struct msghdr msg = { 0 };
+	struct iovec iov;
+	struct cmsghdr *cmsg;
+	char cmsgbuf[CMSG_SPACE(sizeof(cred))];
+	char buf[1];
+	buf[0] = 'p';
+
+	msg.msg_control = cmsgbuf;
+	msg.msg_controllen = sizeof(cmsgbuf);
+
+	cmsg = CMSG_FIRSTHDR(&msg);
+	cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
+	cmsg->cmsg_level = SOL_SOCKET;
+	cmsg->cmsg_type = SCM_CREDENTIALS;
+	memcpy(CMSG_DATA(cmsg), &cred, sizeof(cred));
+
+	msg.msg_name = NULL;
+	msg.msg_namelen = 0;
+
+	iov.iov_base = buf;
+	iov.iov_len = sizeof(buf);
+	msg.msg_iov = &iov;
+	msg.msg_iovlen = 1;
+
+	if (sendmsg(sock, &msg, 0) < 0) {
+		perror("sendmsg");
+		return -1;
+	}
+	return 0;
+}
+
+/*
+ * Get a pid passed in a SCM_CREDENTIAL over a unix socket
+ * @sock: the socket fd.
+ * Credentials are invalid of *p == 1.
+ * Note - this is a synchronous version.  We use it only in the proxy to wait
+ * on the server, since there is no sense not hanging in that case.
+ */
+void get_scm_creds_sync(int sock, uid_t *u, gid_t *g, pid_t *p)
+{
+        struct msghdr msg = { 0 };
+        struct iovec iov;
+        struct cmsghdr *cmsg;
+	struct ucred cred;
+        char cmsgbuf[CMSG_SPACE(sizeof(cred))];
+        char buf[1];
+	int ret, tries=0;
+	int optval = 1;
+
+	cred.pid = -1;
+	cred.uid = -1;
+	cred.gid = -1;
+
+	if (setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval)) == -1) {
+		nih_error("Failed to set passcred: %s", strerror(errno));
+		goto out;
+	}
+	buf[0] = '1';
+	if (write(sock, buf, 1) != 1) {
+		nih_error("Failed to start write on scm fd: %s", strerror(errno));
+		goto out;
+	}
+
+        msg.msg_name = NULL;
+        msg.msg_namelen = 0;
+        msg.msg_control = cmsgbuf;
+        msg.msg_controllen = sizeof(cmsgbuf);
+
+        iov.iov_base = buf;
+        iov.iov_len = sizeof(buf);
+        msg.msg_iov = &iov;
+        msg.msg_iovlen = 1;
+
+	// retry logic is not ideal, especially as we are not
+	// threaded.  Sleep at most 1 second waiting for the client
+	// to send us the scm_cred
+	ret = recvmsg(sock, &msg, 0);
+	if (ret < 0) {
+		nih_error("Failed to receive scm_cred: %s",
+			  strerror(errno));
+		goto out;
+	}
+
+        cmsg = CMSG_FIRSTHDR(&msg);
+
+        if (cmsg && cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred)) &&
+            cmsg->cmsg_level == SOL_SOCKET &&
+            cmsg->cmsg_type == SCM_CREDENTIALS) {
+		memcpy(&cred, CMSG_DATA(cmsg), sizeof(cred));
+        }
+out:
+	*u = cred.uid;
+	*g = cred.gid;
+	*p = cred.pid;
+        return;
+}
+
 int send_pid(int sock, int pid)
 {
 	struct msghdr msg = { 0 };
diff --git a/access_checks.h b/access_checks.h
index c5505f3..00ab303 100644
--- a/access_checks.h
+++ b/access_checks.h
@@ -25,6 +25,8 @@
  */
 
 bool get_nih_io_creds(NihIo *io, struct ucred *ucred);
+int send_creds(int sock, struct ucred cred);
+void get_scm_creds_sync(int sock, uid_t *u, gid_t *g, pid_t *p);
 bool is_same_pidns(int pid);
 bool is_same_userns(int pid);
 bool may_move_pid(pid_t r, uid_t r_uid, pid_t v);
diff --git a/cgmanager-proxy.c b/cgmanager-proxy.c
index f1fe554..01d224b 100644
--- a/cgmanager-proxy.c
+++ b/cgmanager-proxy.c
@@ -117,38 +117,6 @@ unsigned long mypidns;
 bool setns_user_supported = false;
 unsigned long myuserns;
 
-int send_creds(int sock, struct ucred cred)
-{
-	struct msghdr msg = { 0 };
-	struct iovec iov;
-	struct cmsghdr *cmsg;
-	char cmsgbuf[CMSG_SPACE(sizeof(cred))];
-	char buf[1];
-	buf[0] = 'p';
-
-	msg.msg_control = cmsgbuf;
-	msg.msg_controllen = sizeof(cmsgbuf);
-
-	cmsg = CMSG_FIRSTHDR(&msg);
-	cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
-	cmsg->cmsg_level = SOL_SOCKET;
-	cmsg->cmsg_type = SCM_CREDENTIALS;
-	memcpy(CMSG_DATA(cmsg), &cred, sizeof(cred));
-
-	msg.msg_name = NULL;
-	msg.msg_namelen = 0;
-
-	iov.iov_base = buf;
-	iov.iov_len = sizeof(buf);
-	msg.msg_iov = &iov;
-	msg.msg_iovlen = 1;
-
-	if (sendmsg(sock, &msg, 0) < 0) {
-		perror("sendmsg");
-		return -1;
-	}
-	return 0;
-}
 
 void send_dummy_msg(DBusConnection *conn)
 {
@@ -1492,6 +1460,208 @@ int cgmanager_remove (void *data, NihDBusMessage *message,
 	return ret;
 }
 
+/* 
+ * This is one of the dbus callbacks.
+ * Caller requests the number of tasks in @cgroup in @controller
+ * returns nrpids, or -1 on error.
+ */
+int get_tasks_main (void *parent, const char *controller, char *cgroup, struct ucred ucred, int32_t **pids)
+{
+	char buf[1];
+	DBusMessage *message = NULL;
+	DBusMessageIter iter;
+	int sv[2], ret = -1, optval = 1;
+	dbus_uint32_t serial;;
+	uid_t u; gid_t g;
+	uint32_t nrpids;
+	pid_t tmp;
+	int i;
+
+	if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sv) < 0) {
+		nih_error("Error creating socketpair: %s", strerror(errno));
+		return -1;
+	}
+	if (setsockopt(sv[1], SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval)) == -1) {
+		nih_error("setsockopt: %s", strerror(errno));
+		goto out;
+	}
+	if (setsockopt(sv[0], SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval)) == -1) {
+		nih_error("setsockopt: %s", strerror(errno));
+		goto out;
+	}
+
+	message = dbus_message_new_method_call(dbus_bus_get_unique_name(server_conn),
+			"/org/linuxcontainers/cgmanager",
+			"org.linuxcontainers.cgmanager0_0", "getTasksScm");
+
+	dbus_message_iter_init_append(message, &iter);
+        if (! dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &controller)) {
+                nih_error_raise_no_memory ();
+                goto out;
+        }
+        if (! dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &cgroup)) {
+                nih_error_raise_no_memory ();
+                goto out;
+        }
+	if (! dbus_message_iter_append_basic (&iter, DBUS_TYPE_UNIX_FD, &sv[1])) {
+		nih_error_raise_no_memory ();
+		goto out;
+	}
+
+	if (!dbus_connection_send(server_conn, message, &serial)) {
+		nih_error("failed to send dbus message");
+		goto out;
+	}
+	dbus_connection_flush(server_conn);
+	if (message) {
+		dbus_message_unref(message);
+		message = NULL;
+	}
+
+	if (read(sv[0], buf, 1) != 1) {
+		nih_error("Error getting reply from server over socketpair");
+		goto out;
+	}
+	if (send_creds(sv[0], ucred)) {
+		nih_error("Error sending pid over SCM_CREDENTIAL");
+		goto out;
+	}
+	if (read(sv[0], &nrpids, sizeof(uint32_t)) != sizeof(uint32_t))
+		goto out;
+	if (nrpids == 0) {
+		ret = 0;
+		goto out;
+	}
+
+	*pids = nih_alloc(parent, nrpids * sizeof(uint32_t));
+	for (i=0; i<nrpids; i++) {
+		get_scm_creds_sync(sv[0], &u, &g, &tmp);
+		if (tmp == -1) {
+			nih_error("Failed getting pid from server");
+			goto out;
+		}
+		(*pids)[i] = tmp;
+	}
+	ret = nrpids;
+out:
+	close(sv[0]);
+	close(sv[1]);
+	if (message)
+		dbus_message_unref(message);
+	return ret;
+}
+
+void get_tasks_scm_reader (struct scm_sock_data *data,
+		NihIo *io, const char *buf, size_t len)
+{
+	struct ucred ucred, pcred;;
+	int i, ret;
+	int32_t *pids, nrpids;
+
+	if (!get_nih_io_creds(io, &ucred)) {
+		nih_error("failed to read ucred");
+		goto out;
+	}
+	nih_info (_("getTasksScm: Client fd is: %d (pid=%d, uid=%d, gid=%d)"),
+		  data->fd, ucred.pid, ucred.uid, ucred.gid);
+
+	ret = get_tasks_main(data, data->controller, data->cgroup, ucred, &pids);
+	if (ret < 0) {
+		nih_error("Error getting nrtasks for %s:%s for pid %d",
+			data->controller, data->cgroup, ucred.pid);
+		nih_io_shutdown(io);
+		return;
+	}
+	nrpids = ret;
+	if (write(data->fd, &nrpids, sizeof(int32_t)) != sizeof(int32_t)) {
+		nih_error("get_tasks_scm: Error writing final result to client");
+		goto out;
+	}
+
+	pcred.uid = 0; pcred.gid = 0;
+	for (i=0; i<ret; i++) {
+		pcred.pid = pids[i];
+		if (send_creds(data->fd, pcred)) {
+			nih_error("get_tasks_scm: error writing pids back to client");
+			goto out;
+		}
+	}
+out:
+	nih_io_shutdown(io);
+}
+int cgmanager_get_tasks_scm (void *data, NihDBusMessage *message,
+		 const char *controller, char *cgroup, int sockfd)
+{
+	struct scm_sock_data *d;
+        char buf[1];
+	int optval = -1;
+
+	if (setsockopt(sockfd, SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval)) == -1) {
+		nih_dbus_error_raise_printf (DBUS_ERROR_INVALID_ARGS,
+			     "Failed to set passcred: %s", strerror(errno));
+		return -1;
+	}
+	d = nih_alloc(NULL, sizeof(*d));
+	if (!d) {
+		nih_dbus_error_raise_printf (DBUS_ERROR_NO_MEMORY,
+			"Out of memory");
+		return -1;
+	}
+	memset(d, 0, sizeof(*d));
+	d->controller = nih_strdup(d, controller);
+	d->cgroup = nih_strdup(d, cgroup);
+	d->fd = sockfd;
+
+	if (!nih_io_reopen(NULL, sockfd, NIH_IO_MESSAGE,
+		(NihIoReader)get_tasks_scm_reader,
+		(NihIoCloseHandler) scm_sock_close,
+		 NULL, d)) {
+		nih_dbus_error_raise_printf (DBUS_ERROR_INVALID_ARGS,
+			"Failed to queue scm message: %s", strerror(errno));
+		return -1;
+	}
+	buf[0] = '1';
+	if (write(sockfd, buf, 1) != 1) {
+		nih_dbus_error_raise_printf (DBUS_ERROR_INVALID_ARGS,
+			"Failed to start write on scm fd: %s", strerror(errno));
+		return -1;
+	}
+	return 0;
+}
+int cgmanager_get_tasks (void *data, NihDBusMessage *message,
+			 const char *controller, char *cgroup, int32_t **pids, int32_t *nrpids)
+{
+	int fd = 0, ret;
+	struct ucred ucred;
+	socklen_t len;
+
+	if (message == NULL) {
+		nih_dbus_error_raise_printf (DBUS_ERROR_INVALID_ARGS,
+			"message was null");
+		return -1;
+	}
+
+	if (!dbus_connection_get_socket(message->connection, &fd)) {
+		nih_dbus_error_raise_printf (DBUS_ERROR_INVALID_ARGS,
+		                             "Could  not get client socket.");
+		return -1;
+	}
+
+	len = sizeof(struct ucred);
+	NIH_MUST (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) != -1);
+
+	nih_info (_("Remove: Client fd is: %d (pid=%d, uid=%d, gid=%d)"),
+		  fd, ucred.pid, ucred.uid, ucred.gid);
+
+	ret = get_tasks_main(message, controller, cgroup, ucred, pids);
+	if (ret >= 0) {
+		*nrpids = ret;
+		ret = 0;
+	} else
+		nih_dbus_error_raise_printf (DBUS_ERROR_INVALID_ARGS,
+		                             "invalid request");
+	return ret;
+}
 
 int cgmanager_ping (void *data, NihDBusMessage *message, int junk)
 {
diff --git a/cgmanager.c b/cgmanager.c
index d2fbb04..03999a7 100644
--- a/cgmanager.c
+++ b/cgmanager.c
@@ -1395,6 +1395,151 @@ int cgmanager_remove (void *data, NihDBusMessage *message,
 	return ret;
 }
 
+/* 
+ * This is one of the dbus callbacks.
+ * Caller requests the number of tasks in @cgroup in @controller
+ * returns nrpids, or -1 on error.
+ */
+int get_tasks_main (void *parent, const char *controller, char *cgroup, struct ucred ucred, int32_t **pids)
+{
+	char path[MAXPATHLEN];
+	const char *key = "tasks";
+
+	if (!cgroup || ! *cgroup)  // nothing to do
+		return 0;
+	if (!compute_pid_cgroup(ucred.pid, controller, cgroup, path)) {
+		nih_error("Could not determine the requested cgroup");
+		return -1;
+	}
+
+	/* Check access rights to the cgroup directory */
+	if (!may_access(ucred.pid, ucred.uid, ucred.gid, path, O_RDONLY)) {
+		nih_error("Pid %d may not access %s\n", (int)ucred.pid, path);
+		return -1;
+	}
+
+	/* append the filename */
+	if (strlen(path) + strlen(key) + 2 > MAXPATHLEN) {
+		nih_error("filename too long for cgroup %s key %s", path, key);
+		return -1;
+	}
+
+	strncat(path, "/", MAXPATHLEN-1);
+	strncat(path, key, MAXPATHLEN-1);
+
+	return file_read_pids(parent, path, pids);
+}
+
+void get_tasks_scm_reader (struct scm_sock_data *data,
+		NihIo *io, const char *buf, size_t len)
+{
+	struct ucred ucred, pcred;
+	int i, ret;
+	int32_t *pids, nrpids;
+
+	if (!get_nih_io_creds(io, &ucred)) {
+		nih_error("failed to read ucred");
+		goto out;
+	}
+	nih_info (_("getTasksScm: Client fd is: %d (pid=%d, uid=%d, gid=%d)"),
+		  data->fd, ucred.pid, ucred.uid, ucred.gid);
+
+	ret = get_tasks_main(data, data->controller, data->cgroup, ucred, &pids);
+	if (ret < 0) {
+		nih_error("Error getting nrtasks for %s:%s for pid %d",
+			data->controller, data->cgroup, ucred.pid);
+		nih_io_shutdown(io);
+		return;
+	}
+	nrpids = ret;
+	if (write(data->fd, &nrpids, sizeof(int32_t)) != sizeof(int32_t)) {
+		nih_error("get_tasks_scm: Error writing final result to client");
+		goto out;
+	}
+	pcred.uid = 0; pcred.gid = 0;
+	for (i=0; i<ret; i++) {
+		pcred.pid = pids[i];
+		if (send_creds(data->fd, pcred)) {
+			nih_error("get_tasks_scm: error writing pids back to client");
+			goto out;
+		}
+	}
+out:
+	nih_io_shutdown(io);
+}
+int cgmanager_get_tasks_scm (void *data, NihDBusMessage *message,
+		 const char *controller, char *cgroup, int sockfd)
+{
+	struct scm_sock_data *d;
+        char buf[1];
+	int optval = -1;
+
+	if (setsockopt(sockfd, SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval)) == -1) {
+		nih_dbus_error_raise_printf (DBUS_ERROR_INVALID_ARGS,
+			     "Failed to set passcred: %s", strerror(errno));
+		return -1;
+	}
+	d = nih_alloc(NULL, sizeof(*d));
+	if (!d) {
+		nih_dbus_error_raise_printf (DBUS_ERROR_NO_MEMORY,
+			"Out of memory");
+		return -1;
+	}
+	memset(d, 0, sizeof(*d));
+	d->controller = nih_strdup(d, controller);
+	d->cgroup = nih_strdup(d, cgroup);
+	d->fd = sockfd;
+
+	if (!nih_io_reopen(NULL, sockfd, NIH_IO_MESSAGE,
+		(NihIoReader)get_tasks_scm_reader,
+		(NihIoCloseHandler) scm_sock_close,
+		 NULL, d)) {
+		nih_dbus_error_raise_printf (DBUS_ERROR_INVALID_ARGS,
+			"Failed to queue scm message: %s", strerror(errno));
+		return -1;
+	}
+	buf[0] = '1';
+	if (write(sockfd, buf, 1) != 1) {
+		nih_dbus_error_raise_printf (DBUS_ERROR_INVALID_ARGS,
+			"Failed to start write on scm fd: %s", strerror(errno));
+		return -1;
+	}
+	return 0;
+}
+int cgmanager_get_tasks (void *data, NihDBusMessage *message,
+			 const char *controller, char *cgroup, int32_t **pids, int32_t *nrpids)
+{
+	int fd = 0, ret;
+	struct ucred ucred;
+	socklen_t len;
+
+	if (message == NULL) {
+		nih_dbus_error_raise_printf (DBUS_ERROR_INVALID_ARGS,
+			"message was null");
+		return -1;
+	}
+
+	if (!dbus_connection_get_socket(message->connection, &fd)) {
+		nih_dbus_error_raise_printf (DBUS_ERROR_INVALID_ARGS,
+		                             "Could  not get client socket.");
+		return -1;
+	}
+
+	len = sizeof(struct ucred);
+	NIH_MUST (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) != -1);
+
+	nih_info (_("Remove: Client fd is: %d (pid=%d, uid=%d, gid=%d)"),
+		  fd, ucred.pid, ucred.uid, ucred.gid);
+
+	ret = get_tasks_main(message, controller, cgroup, ucred, pids);
+	if (ret >= 0) {
+		*nrpids = ret;
+		ret = 0;
+	} else
+		nih_dbus_error_raise_printf (DBUS_ERROR_INVALID_ARGS,
+		                             "invalid request");
+	return ret;
+}
 
 static dbus_bool_t allow_user(DBusConnection *connection, unsigned long uid, void *data)
 {
diff --git a/configure.ac b/configure.ac
index 31c1e3d..4ce8152 100644
--- a/configure.ac
+++ b/configure.ac
@@ -12,6 +12,8 @@ AM_INIT_AUTOMAKE
 LT_INIT
 AC_PROG_CC
 
+AC_PROG_CC_C99
+
 AC_PATH_PROG([NIH_DBUS_TOOL], [nih-dbus-tool])
 
 PKG_CHECK_MODULES([NIH], [libnih >= 1.0.2])
diff --git a/fs.c b/fs.c
index 7494766..6fdd925 100644
--- a/fs.c
+++ b/fs.c
@@ -469,6 +469,47 @@ out:
 }
 
 /*
+ * file_read_pids:
+ *
+ * @parent: parent which will be given a reference to the returned string
+ * (to allow the returned value to be freed automatically when @parent is
+ * freed).
+ * @path: Full path to file to read.
+ *
+ * Read specified file and return the pids it contains.  The file is
+ * expected to contain only a set of newline-separated int32_ts.
+ *
+ * Returns: Number of pids read, which are placed into the newly allocated
+ * pids array (passed in).
+ */
+int file_read_pids(void *parent, const char *path, int32_t **pids)
+{
+	int nrpids = 0, pid;
+	FILE *fin = fopen(path, "r");
+
+	pids = NULL;
+	if (!fin) {
+		nih_error("Error opening %s: %s", path, strerror(errno));
+		return -1;
+	}
+
+	while (fscanf(fin, "%d", &pid) == 1) {
+		int32_t *tmp;
+		if (!(tmp = nih_realloc(pids, parent, (nrpids+1)*sizeof(int32_t)))) {
+			if (pids)
+				nih_free(pids);
+			pids = NULL;
+			goto out;
+		}
+		pids = tmp;
+		pids[nrpids++] = (int32_t) pid;
+	}
+out:
+	fclose(fin);
+	return nrpids;
+}
+
+/*
  * get_pid_creds: get the real uid and gid of @pid from
  * /proc/$$/status
  * (XXX should we use euid here?)
diff --git a/fs.h b/fs.h
index 46ffe54..16c288a 100644
--- a/fs.h
+++ b/fs.h
@@ -29,6 +29,7 @@ bool compute_pid_cgroup(pid_t pid, const char *controller, const char *cgroup, c
 bool may_access(pid_t pid, uid_t uid, gid_t gid, const char *path, int mode);
 void get_pid_creds(pid_t pid, uid_t *uid, gid_t *gid);
 char *file_read_string(void *parent, const char *path);
+int file_read_pids(void *parent, const char *path, int32_t **pids);
 void get_pid_creds(pid_t pid, uid_t *uid, gid_t *gid);
 const char *get_controller_path(const char *controller);
 uid_t hostuid_to_ns(uid_t uid, pid_t pid);
diff --git a/org.linuxcontainers.cgmanager.xml b/org.linuxcontainers.cgmanager.xml
index 42485d1..a684bf2 100644
--- a/org.linuxcontainers.cgmanager.xml
+++ b/org.linuxcontainers.cgmanager.xml
@@ -120,6 +120,17 @@
       <arg name="recursive" type="i" direction="in" />
       <arg name="existed" type="i" direction="out" />
     </method>
+    <method name="getTasksScm">
+      <arg name="controller" type="s" direction="in" />
+      <arg name="cgroup" type="s" direction="in" />
+      <arg name="sockfd" type="h" direction="in" />
+      <!-- nrtasks + pids as scm creds return value comes over sockfd -->
+    </method>
+    <method name="getTasks">
+      <arg name="controller" type="s" direction="in" />
+      <arg name="cgroup" type="s" direction="in" />
+      <arg name="output" type="ai" direction="out" />
+    </method>
     <!-- still to add: low priority,
 	 Remove
 	 Prune (remove all empty decendents)
-- 
1.8.5.2



More information about the cgmanager-devel mailing list