[cgmanager-devel] [PATCH RFC] implement list_children
Serge Hallyn
serge.hallyn at ubuntu.com
Thu Feb 27 21:45:38 UTC 2014
Signed-off-by: Serge Hallyn <serge.hallyn at ubuntu.com>
---
cgmanager-proxy.c | 76 +++++++++++++++++++++++
cgmanager.c | 24 ++++++++
frontend.c | 125 ++++++++++++++++++++++++++++++++++++++
frontend.h | 4 ++
fs.c | 56 +++++++++++++++++
fs.h | 1 +
org.linuxcontainers.cgmanager.xml | 12 +++-
7 files changed, 297 insertions(+), 1 deletion(-)
diff --git a/cgmanager-proxy.c b/cgmanager-proxy.c
index a405d6e..832dc68 100644
--- a/cgmanager-proxy.c
+++ b/cgmanager-proxy.c
@@ -735,6 +735,82 @@ out:
return ret;
}
+int list_children_main (void *parent, const char *controller, const char *cgroup,
+ struct ucred p, struct ucred r, char ***output)
+{
+ DBusMessage *message;
+ DBusMessageIter iter;
+ int sv[2], ret = -1;
+ uint32_t len, nrkids;
+ nih_local char * paths;
+ char *s;
+ int i;
+
+ *output = NULL;
+ if (memcmp(&p, &r, sizeof(struct ucred)) != 0) {
+ nih_error("%s: proxy != requestor", __func__);
+ return -1;
+ }
+
+ if (!sane_cgroup(cgroup)) {
+ nih_error("unsafe cgroup");
+ return -1;
+ }
+
+ if (!(message = start_dbus_request("ListChildrenScm", sv))) {
+ nih_error("%s: error starting dbus request", __func__);
+ return -1;
+ }
+
+ dbus_message_iter_init_append(message, &iter);
+ if (! dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &controller)) {
+ nih_error("%s: out of memory", __func__);
+ goto out;
+ }
+ if (! dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &cgroup)) {
+ nih_error("%s: out of memory", __func__);
+ goto out;
+ }
+ if (! dbus_message_iter_append_basic (&iter, DBUS_TYPE_UNIX_FD, &sv[1])) {
+ nih_error("%s: out of memory", __func__);
+ goto out;
+ }
+
+ if (!complete_dbus_request(message, sv, &r, NULL)) {
+ nih_error("%s: error completing dbus request", __func__);
+ goto out;
+ }
+
+ if (recv(sv[0], &nrkids, sizeof(uint32_t), 0) != sizeof(uint32_t))
+ goto out;
+ if (nrkids == 0) {
+ ret = 0;
+ goto out;
+ }
+ if (recv(sv[0], &len, sizeof(uint32_t), 0) != sizeof(uint32_t))
+ goto out;
+
+ paths = nih_alloc(NULL, len);
+ if (read(sv[0], paths, len) != len) {
+ nih_error("%s: Failed getting paths from server", __func__);
+ goto out;
+ }
+
+ *output = NIH_MUST( nih_alloc(parent, sizeof( char*)*(nrkids+1)) );
+
+ s = paths;
+ (*output)[nrkids] = NULL;
+ for (i=0; i<nrkids; i++) {
+ (*output)[i] = NIH_MUST( nih_strdup(parent, s) );
+ s += strlen(s) + 1;
+ }
+ ret = nrkids;
+out:
+ close(sv[0]);
+ close(sv[1]);
+ return ret;
+}
+
/**
* options:
*
diff --git a/cgmanager.c b/cgmanager.c
index 7c38c25..aa69b7e 100644
--- a/cgmanager.c
+++ b/cgmanager.c
@@ -641,6 +641,30 @@ int get_tasks_main(void *parent, const char *controller, const char *cgroup,
return file_read_pids(parent, path, pids);
}
+int list_children_main(void *parent, const char *controller, const char *cgroup,
+ struct ucred p, struct ucred r, char ***output)
+{
+ char path[MAXPATHLEN];
+
+ if (!sane_cgroup(cgroup)) {
+ nih_error("unsafe cgroup");
+ return -1;
+ }
+
+ if (!compute_pid_cgroup(r.pid, controller, cgroup, path, NULL)) {
+ nih_error("Could not determine the requested cgroup");
+ return -1;
+ }
+
+ /* Check access rights to the cgroup directory */
+ if (!may_access(r.pid, r.uid, r.gid, path, O_RDONLY)) {
+ nih_error("Pid %d may not access %s\n", r.pid, path);
+ return -1;
+ }
+
+ return get_child_directories(parent, path, output);
+}
+
char *extra_cgroup_mounts;
static int
diff --git a/frontend.c b/frontend.c
index 3f618e7..d3ed8e8 100644
--- a/frontend.c
+++ b/frontend.c
@@ -89,6 +89,7 @@ static const char *req_type_to_str(enum req_type r)
case REQ_TYPE_REMOVE: return "remove";
case REQ_TYPE_GET_TASKS: return "get_tasks";
case REQ_TYPE_CHMOD: return "chmod";
+ case REQ_TYPE_LIST_CHILDREN: return "list_children";
default: return "invalid";
}
}
@@ -182,6 +183,7 @@ static void sock_scm_reader(struct scm_sock_data *data,
case REQ_TYPE_SET_VALUE: set_value_complete(data); break;
case REQ_TYPE_REMOVE: remove_scm_complete(data); break;
case REQ_TYPE_GET_TASKS: get_tasks_scm_complete(data); break;
+ case REQ_TYPE_LIST_CHILDREN: list_children_scm_complete(data); break;
default:
nih_fatal("%s: bad req_type %d", __func__, data->type);
exit(1);
@@ -1170,6 +1172,129 @@ int cgmanager_get_tasks (void *data, NihDBusMessage *message, const char *contro
return ret;
}
+void list_children_scm_complete(struct scm_sock_data *data)
+{
+ int i, ret;
+ uint32_t len = 0, remainlen, nrkids;
+ char **output; // nih_alloced with data as parent; freed at io_shutdown
+ nih_local char * path;
+ char *p;
+
+ ret = list_children_main(data, data->controller, data->cgroup,
+ data->pcred, data->rcred, &output);
+ if (ret < 0) {
+ nih_error("Error getting children for %s:%s for pid %d",
+ data->controller, data->cgroup, data->rcred.pid);
+ return;
+ }
+ nrkids = ret;
+ for (i=0; i < nrkids; i++)
+ len += strlen(output[i]) + 1;
+ path = nih_alloc(NULL, len);
+ if (!path) {
+ nih_error("Out of memory");
+ return;
+ }
+ p = path;
+ remainlen = len;
+ for (i=0; i < nrkids; i++) {
+ ret = snprintf(p, remainlen, "%s", output[i]);
+ if (ret < 0 || ret >= remainlen) // bogus
+ return;
+ p += ret + 1;
+ remainlen -= ret + 1;
+ }
+
+ if (write(data->fd, &nrkids, sizeof(uint32_t)) != sizeof(uint32_t)) {
+ nih_error("%s: error writing results", __func__);
+ return;
+ }
+ if (write(data->fd, &len, sizeof(uint32_t)) != sizeof(uint32_t)) {
+ nih_error("%s: error writing results", __func__);
+ return;
+ }
+
+ if (write(data->fd, path, len) != len) {
+ nih_error("list_children_scm: Error writing final result to client");
+ return;
+ }
+}
+
+int cgmanager_list_children_scm (void *data, NihDBusMessage *message,
+ const char *controller, const char *cgroup, int sockfd)
+{
+ struct scm_sock_data *d;
+
+ d = alloc_scm_sock_data(message, sockfd, REQ_TYPE_LIST_CHILDREN);
+ if (!d)
+ return -1;
+ d->controller = NIH_MUST( nih_strdup(d, controller) );
+ d->cgroup = NIH_MUST( nih_strdup(d, cgroup) );
+
+ if (!nih_io_reopen(NULL, sockfd, NIH_IO_MESSAGE,
+ (NihIoReader) sock_scm_reader,
+ (NihIoCloseHandler) scm_sock_close,
+ scm_sock_error_handler, d)) {
+ NihError *error = nih_error_steal ();
+ nih_dbus_error_raise_printf (DBUS_ERROR_INVALID_ARGS,
+ "Failed queue scm message: %s", error->message);
+ nih_free(error);
+ return -1;
+ }
+ if (!kick_fd_client(sockfd)) {
+ nih_dbus_error_raise_printf (DBUS_ERROR_INVALID_ARGS,
+ "Error writing to client: %s", strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * This is one of the dbus callbacks.
+ * Caller requests the number of tasks in @cgroup in @controller
+ * returns nrpids, or -1 on error.
+ */
+int cgmanager_list_children (void *data, NihDBusMessage *message,
+ const char *controller, const char *cgroup, char ***output)
+{
+ int fd = 0, ret;
+ struct ucred rcred;
+ socklen_t len;
+
+ nih_assert(output);
+
+ 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);
+ if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &rcred, &len) < 0) {
+ nih_dbus_error_raise_printf (DBUS_ERROR_INVALID_ARGS,
+ "Could not get peer cred: %s",
+ strerror(errno));
+ return -1;
+ }
+
+ nih_info (_("ListChildren: Client fd is: %d (pid=%d, uid=%u, gid=%u)"),
+ fd, rcred.pid, rcred.uid, rcred.gid);
+
+ ret = list_children_main(message, controller, cgroup, rcred, rcred, output);
+ if (ret >= 0)
+ ret = 0;
+ else
+ nih_dbus_error_raise_printf (DBUS_ERROR_INVALID_ARGS,
+ "invalid request");
+ return ret;
+}
+
int
cgmanager_get_api_version(void *data, NihDBusMessage *message, int *version)
{
diff --git a/frontend.h b/frontend.h
index 4f39d3f..935231f 100644
--- a/frontend.h
+++ b/frontend.h
@@ -102,6 +102,7 @@ enum req_type {
REQ_TYPE_GET_TASKS,
REQ_TYPE_CHMOD,
REQ_TYPE_MOVE_PID_ABS,
+ REQ_TYPE_LIST_CHILDREN,
};
int get_pid_cgroup_main(void *parent, const char *controller,
@@ -136,6 +137,9 @@ void remove_scm_complete(struct scm_sock_data *data);
int get_tasks_main (void *parent, const char *controller, const char *cgroup,
struct ucred p, struct ucred r, int32_t **pids);
void get_tasks_scm_complete(struct scm_sock_data *data);
+int list_children_main (void *parent, const char *controller, const char *cgroup,
+ struct ucred p, struct ucred r, char ***output);
+void list_children_scm_complete(struct scm_sock_data *data);
int cgmanager_ping (void *data, NihDBusMessage *message, int junk);
diff --git a/fs.c b/fs.c
index 32a06a5..821af8c 100644
--- a/fs.c
+++ b/fs.c
@@ -887,3 +887,59 @@ bool move_self_to_root(void)
}
return true;
}
+
+/*
+ * get_child_directories:
+ *
+ * @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 whose child directories to list.
+ * output: pointer to which the list of directory names will be stored.
+ *
+ * Read all child directories under @path.
+ *
+ * Returns: Number of directories read. The names will be placed in the
+ * null-terminated array @output.
+ */
+int get_child_directories(void *parent, const char *path, char ***output)
+{
+ int used = 0, alloced = 5;
+ DIR *d;
+ struct dirent dirent, *direntp;
+
+ nih_assert(output);
+ d = opendir(path);
+ if (!d) {
+ nih_error("%s: failed to open directory %s: %s",
+ __func__, path, strerror(errno));
+ return -1;
+ }
+ *output = NIH_MUST( nih_alloc(parent, alloced * sizeof(char *)) );
+ (*output)[0] = NULL;
+ while (readdir_r(d, &dirent, &direntp) == 0 && direntp) {
+ if (!strcmp(direntp->d_name, ".") || !strcmp(direntp->d_name, ".."))
+ continue;
+ if (DT_DIR != direntp->d_type)
+ continue;
+ if (used+1 >= alloced) {
+ char **tmp;
+ alloced += 5;
+ tmp = nih_realloc(*output, parent, alloced * sizeof(char *));
+ if (!tmp) {
+ if (*output)
+ nih_free(*output);
+ output = NULL;
+ nih_error("%s: Out of memory", __func__);
+ closedir(d);
+ return -1;
+ }
+ *output = tmp;
+ }
+ (*output)[used] = NIH_MUST( nih_strdup(parent, direntp->d_name) );
+ (*output)[used+1] = NULL;
+ used++;
+ }
+ closedir(d);
+ return used;
+}
diff --git a/fs.h b/fs.h
index 6067e23..d595613 100644
--- a/fs.h
+++ b/fs.h
@@ -44,3 +44,4 @@ bool realpath_escapes(char *path, char *safety);
bool file_exists(const char *path);
bool dir_exists(const char *path);
bool move_self_to_root(void);
+int get_child_directories(void *parent, const char *path, char ***output);
diff --git a/org.linuxcontainers.cgmanager.xml b/org.linuxcontainers.cgmanager.xml
index bd31c24..619125b 100644
--- a/org.linuxcontainers.cgmanager.xml
+++ b/org.linuxcontainers.cgmanager.xml
@@ -160,10 +160,20 @@
<arg name="cgroup" type="s" direction="in" />
<arg name="output" type="ai" direction="out" />
</method>
+ <method name="ListChildrenScm">
+ <arg name="controller" type="s" direction="in" />
+ <arg name="cgroup" type="s" direction="in" />
+ <arg name="sockfd" type="h" direction="in" />
+ <!-- names will be returned over sockfd -->
+ </method>
+ <method name="ListChildren">
+ <arg name="controller" type="s" direction="in" />
+ <arg name="cgroup" type="s" direction="in" />
+ <arg name="output" type="as" direction="out" />
+ </method>
<!-- still to add: low priority,
Prune (remove all empty decendents)
removeWhenEmpty
- listChildren
getEventfd
-->
<property name="api_version" type="i" access="read" />
--
1.9.0
More information about the cgmanager-devel
mailing list