- struct file_info *d = (struct file_info *)fi->fh;
- struct cgm_keys **list = NULL;
- int i, ret;
- char *nextcg = NULL;
- struct fuse_context *fc = fuse_get_context();
- char **clist = NULL;
-
- if (d->type != LXC_TYPE_CGDIR) {
- fprintf(stderr, "Internal error: file cache info used in readdir\n");
- return -EIO;
- }
- if (!d->cgroup && !d->controller) {
- // ls /var/lib/lxcfs/cgroup - just show list of controllers
- char **list = LXCFS_DATA ? LXCFS_DATA->subsystems : NULL;
- int i;
-
- if (!list)
- return -EIO;
-
- for (i = 0; list[i]; i++) {
- if (filler(buf, list[i], NULL, 0) != 0) {
- return -EIO;
- }
- }
- return 0;
- }
-
- if (!cgm_list_keys(d->controller, d->cgroup, &list)) {
- // not a valid cgroup
- ret = -EINVAL;
- goto out;
- }
-
- if (!caller_is_in_ancestor(fc->pid, d->controller, d->cgroup, &nextcg)) {
- if (nextcg) {
- int ret;
- ret = filler(buf, nextcg, NULL, 0);
- free(nextcg);
- if (ret != 0) {
- ret = -EIO;
- goto out;
- }
- }
- ret = 0;
- goto out;
- }
-
- for (i = 0; list[i]; i++) {
- if (filler(buf, list[i]->name, NULL, 0) != 0) {
- ret = -EIO;
- goto out;
- }
- }
-
- // now get the list of child cgroups
-
- if (!cgm_list_children(d->controller, d->cgroup, &clist)) {
- ret = 0;
- goto out;
- }
- for (i = 0; clist[i]; i++) {
- if (filler(buf, clist[i], NULL, 0) != 0) {
- ret = -EIO;
- goto out;
- }
- }
- ret = 0;
-
-out:
- free_keys(list);
- if (clist) {
- for (i = 0; clist[i]; i++)
- free(clist[i]);
- free(clist);
- }
- return ret;
-}
-
-static void do_release_file_info(struct file_info *f)
-{
- if (!f)
- return;
- free(f->controller);
- free(f->cgroup);
- free(f->file);
- free(f->buf);
- free(f);
-}
-
-static int cg_releasedir(const char *path, struct fuse_file_info *fi)
-{
- struct file_info *d = (struct file_info *)fi->fh;
-
- do_release_file_info(d);
- return 0;
-}
-
-static int cg_open(const char *path, struct fuse_file_info *fi)
-{
- const char *cgroup;
- char *fpath = NULL, *path1, *path2, * cgdir = NULL, *controller;
- struct cgm_keys *k = NULL;
- struct file_info *file_info;
- struct fuse_context *fc = fuse_get_context();
- int ret;
-
- if (!fc)
- return -EIO;
-
- controller = pick_controller_from_path(fc, path);
- if (!controller)
- return -EIO;
- cgroup = find_cgroup_in_path(path);
- if (!cgroup)
- return -EINVAL;
-
- get_cgdir_and_path(cgroup, &cgdir, &fpath);
- if (!fpath) {
- path1 = "/";
- path2 = cgdir;
- } else {
- path1 = cgdir;
- path2 = fpath;
- }
-
- k = get_cgroup_key(controller, path1, path2);
- if (!k) {
- ret = -EINVAL;
- goto out;
- }
- free_key(k);
-
- if (!fc_may_access(fc, controller, path1, path2, fi->flags)) {
- // should never get here
- ret = -EACCES;
- goto out;
- }
-
- /* we'll free this at cg_release */
- file_info = malloc(sizeof(*file_info));
- if (!file_info) {
- ret = -ENOMEM;
- goto out;
- }
- file_info->controller = must_copy_string(file_info, controller);
- file_info->cgroup = must_copy_string(file_info, path1);
- file_info->file = must_copy_string(file_info, path2);
- file_info->type = LXC_TYPE_CGFILE;
- file_info->buf = NULL;
- file_info->buflen = 0;
-
- fi->fh = (unsigned long)file_info;
- ret = 0;
-
-out:
- free(cgdir);
- return ret;
-}
-
-static int cg_release(const char *path, struct fuse_file_info *fi)
-{
- struct file_info *f = (struct file_info *)fi->fh;
-
- do_release_file_info(f);
- return 0;
-}
-
-static int msgrecv(int sockfd, void *buf, size_t len)
-{
- struct timeval tv;
- fd_set rfds;
-
- FD_ZERO(&rfds);
- FD_SET(sockfd, &rfds);
- tv.tv_sec = 2;
- tv.tv_usec = 0;
-
- if (select(sockfd+1, &rfds, NULL, NULL, &tv) <= 0)
- return -1;
- return recv(sockfd, buf, len, MSG_DONTWAIT);
-}
-
-#define SEND_CREDS_OK 0
-#define SEND_CREDS_NOTSK 1
-#define SEND_CREDS_FAIL 2
-static int send_creds(int sock, struct ucred *cred, char v, bool pingfirst)
-{
- struct msghdr msg = { 0 };
- struct iovec iov;
- struct cmsghdr *cmsg;
- char cmsgbuf[CMSG_SPACE(sizeof(*cred))];
- char buf[1];
- buf[0] = 'p';
-
- if (pingfirst) {
- if (msgrecv(sock, buf, 1) != 1) {
- fprintf(stderr, "%s: Error getting reply from server over socketpair\n",
- __func__);
- return SEND_CREDS_FAIL;
- }
- }
-
- 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;
-
- buf[0] = v;
- iov.iov_base = buf;
- iov.iov_len = sizeof(buf);
- msg.msg_iov = &iov;
- msg.msg_iovlen = 1;
-
- if (sendmsg(sock, &msg, 0) < 0) {
- fprintf(stderr, "%s: failed at sendmsg: %s\n", __func__,
- strerror(errno));
- if (errno == 3)
- return SEND_CREDS_NOTSK;
- return SEND_CREDS_FAIL;
- }
-
- return SEND_CREDS_OK;
-}
-
-static bool recv_creds(int sock, struct ucred *cred, char *v)
-{
- struct msghdr msg = { 0 };
- struct iovec iov;
- struct cmsghdr *cmsg;
- char cmsgbuf[CMSG_SPACE(sizeof(*cred))];
- char buf[1];
- int ret;
- int optval = 1;
- struct timeval tv;
- fd_set rfds;
-
- *v = '1';
-
- cred->pid = -1;
- cred->uid = -1;
- cred->gid = -1;
-
- if (setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval)) == -1) {
- fprintf(stderr, "Failed to set passcred: %s\n", strerror(errno));
- return false;
- }
- buf[0] = '1';
- if (write(sock, buf, 1) != 1) {
- fprintf(stderr, "Failed to start write on scm fd: %s\n", strerror(errno));
- return false;
- }
-
- 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;
-
- FD_ZERO(&rfds);
- FD_SET(sock, &rfds);
- tv.tv_sec = 2;
- tv.tv_usec = 0;
- if (select(sock+1, &rfds, NULL, NULL, &tv) <= 0) {
- fprintf(stderr, "Failed to select for scm_cred: %s\n",
- strerror(errno));
- return false;
- }
- ret = recvmsg(sock, &msg, MSG_DONTWAIT);
- if (ret < 0) {
- fprintf(stderr, "Failed to receive scm_cred: %s\n",
- strerror(errno));
- return false;
- }
-
- 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));
- }
- *v = buf[0];
-
- return true;
-}
-
-
-/*
- * pid_to_ns - reads pids from a ucred over a socket, then writes the
- * int value back over the socket. This shifts the pid from the
- * sender's pidns into tpid's pidns.
- */
-static void pid_to_ns(int sock, pid_t tpid)
-{
- char v = '0';
- struct ucred cred;
-
- while (recv_creds(sock, &cred, &v)) {
- if (v == '1')
- _exit(0);
- if (write(sock, &cred.pid, sizeof(pid_t)) != sizeof(pid_t))
- _exit(1);
- }
- _exit(0);
-}
-
-/*
- * pid_to_ns_wrapper: when you setns into a pidns, you yourself remain
- * in your old pidns. Only children which you fork will be in the target
- * pidns. So the pid_to_ns_wrapper does the setns, then forks a child to
- * actually convert pids
- */
-static void pid_to_ns_wrapper(int sock, pid_t tpid)
-{
- int newnsfd = -1, ret, cpipe[2];
- char fnam[100];
- pid_t cpid;
- struct timeval tv;
- fd_set s;
- char v;
-
- ret = snprintf(fnam, sizeof(fnam), "/proc/%d/ns/pid", tpid);
- if (ret < 0 || ret >= sizeof(fnam))
- _exit(1);
- newnsfd = open(fnam, O_RDONLY);
- if (newnsfd < 0)
- _exit(1);
- if (setns(newnsfd, 0) < 0)
- _exit(1);
- close(newnsfd);
-
- if (pipe(cpipe) < 0)
- _exit(1);
-
-loop:
- cpid = fork();
- if (cpid < 0)
- _exit(1);
-
- if (!cpid) {
- char b = '1';
- close(cpipe[0]);
- if (write(cpipe[1], &b, sizeof(char)) < 0) {
- fprintf(stderr, "%s (child): erorr on write: %s\n",
- __func__, strerror(errno));
- }
- close(cpipe[1]);
- pid_to_ns(sock, tpid);
- }
- // give the child 1 second to be done forking and
- // write it's ack
- FD_ZERO(&s);
- FD_SET(cpipe[0], &s);
- tv.tv_sec = 1;
- tv.tv_usec = 0;
- ret = select(cpipe[0]+1, &s, NULL, NULL, &tv);
- if (ret <= 0)
- goto again;
- ret = read(cpipe[0], &v, 1);
- if (ret != sizeof(char) || v != '1') {
- goto again;
- }
-
- if (!wait_for_pid(cpid))
- _exit(1);
- _exit(0);
-
-again:
- kill(cpid, SIGKILL);
- wait_for_pid(cpid);
- goto loop;
-}
-
-/*
- * To read cgroup files with a particular pid, we will setns into the child
- * pidns, open a pipe, fork a child - which will be the first to really be in
- * the child ns - which does the cgm_get_value and writes the data to the pipe.
- */
-static bool do_read_pids(pid_t tpid, const char *contrl, const char *cg, const char *file, char **d)
-{
- int sock[2] = {-1, -1};
- char *tmpdata = NULL;
- int ret;
- pid_t qpid, cpid = -1;
- bool answer = false;
- char v = '0';
- struct ucred cred;
- struct timeval tv;
- size_t sz = 0, asz = 0;
- fd_set s;
-
- if (!cgm_get_value(contrl, cg, file, &tmpdata))
- return false;
-
- /*
- * Now we read the pids from returned data one by one, pass
- * them into a child in the target namespace, read back the
- * translated pids, and put them into our to-return data
- */
-
- if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sock) < 0) {
- perror("socketpair");
- free(tmpdata);
- return false;
- }
-
- cpid = fork();
- if (cpid == -1)
- goto out;
-
- if (!cpid) // child
- pid_to_ns_wrapper(sock[1], tpid);
-
- char *ptr = tmpdata;
- cred.uid = 0;
- cred.gid = 0;
- while (sscanf(ptr, "%d\n", &qpid) == 1) {
- cred.pid = qpid;
- ret = send_creds(sock[0], &cred, v, true);
-
- if (ret == SEND_CREDS_NOTSK)
- goto next;
- if (ret == SEND_CREDS_FAIL)
- goto out;
-
- // read converted results
- FD_ZERO(&s);
- FD_SET(sock[0], &s);
- tv.tv_sec = 2;
- tv.tv_usec = 0;
- ret = select(sock[0]+1, &s, NULL, NULL, &tv);
- if (ret <= 0) {
- fprintf(stderr, "%s: select error waiting for pid from child: %s\n",
- __func__, strerror(errno));
- goto out;
- }
- if (read(sock[0], &qpid, sizeof(qpid)) != sizeof(qpid)) {
- fprintf(stderr, "%s: error reading pid from child: %s\n",
- __func__, strerror(errno));
- goto out;
- }
- must_strcat_pid(d, &sz, &asz, qpid);
-next:
- ptr = strchr(ptr, '\n');
- if (!ptr)
- break;
- ptr++;
- }
-
- cred.pid = getpid();
- v = '1';
- if (send_creds(sock[0], &cred, v, true) != SEND_CREDS_OK) {
- // failed to ask child to exit
- fprintf(stderr, "%s: failed to ask child to exit: %s\n",
- __func__, strerror(errno));
- goto out;
- }
-
- answer = true;
-
-out:
- free(tmpdata);
- if (cpid != -1)
- wait_for_pid(cpid);
- if (sock[0] != -1) {
- close(sock[0]);
- close(sock[1]);
- }
- return answer;
-}
-
-static int cg_read(const char *path, char *buf, size_t size, off_t offset,
- struct fuse_file_info *fi)
-{
- struct fuse_context *fc = fuse_get_context();
- struct file_info *f = (struct file_info *)fi->fh;
- struct cgm_keys *k = NULL;
- char *data = NULL;
- int ret, s;
- bool r;
-
- if (f->type != LXC_TYPE_CGFILE) {
- fprintf(stderr, "Internal error: directory cache info used in cg_read\n");
- return -EIO;
- }
-
- if (offset)
- return 0;
-
- if (!fc)
- return -EIO;
-
- if (!f->controller)
- return -EINVAL;
-
- if ((k = get_cgroup_key(f->controller, f->cgroup, f->file)) == NULL) {
- return -EINVAL;
- }
- free_key(k);
-
-
- if (!fc_may_access(fc, f->controller, f->cgroup, f->file, O_RDONLY)) { // should never get here
- ret = -EACCES;
- goto out;
- }
-
- if (strcmp(f->file, "tasks") == 0 ||
- strcmp(f->file, "/tasks") == 0 ||
- strcmp(f->file, "/cgroup.procs") == 0 ||
- strcmp(f->file, "cgroup.procs") == 0)
- // special case - we have to translate the pids
- r = do_read_pids(fc->pid, f->controller, f->cgroup, f->file, &data);
- else
- r = cgm_get_value(f->controller, f->cgroup, f->file, &data);
-
- if (!r) {
- ret = -EINVAL;
- goto out;
- }
-
- if (!data) {
- ret = 0;
- goto out;
- }
- s = strlen(data);
- if (s > size)
- s = size;
- memcpy(buf, data, s);
- if (s > 0 && s < size && data[s-1] != '\n')
- buf[s++] = '\n';
-
- ret = s;
-
-out:
- free(data);
- return ret;
-}
-
-static void pid_from_ns(int sock, pid_t tpid)
-{
- pid_t vpid;
- struct ucred cred;
- char v;
- struct timeval tv;
- fd_set s;
- int ret;
-
- cred.uid = 0;
- cred.gid = 0;
- while (1) {
- FD_ZERO(&s);
- FD_SET(sock, &s);
- tv.tv_sec = 2;
- tv.tv_usec = 0;
- ret = select(sock+1, &s, NULL, NULL, &tv);
- if (ret <= 0) {
- fprintf(stderr, "%s: bad select before read from parent: %s\n",
- __func__, strerror(errno));
- _exit(1);
- }
- if ((ret = read(sock, &vpid, sizeof(pid_t))) != sizeof(pid_t)) {
- fprintf(stderr, "%s: bad read from parent: %s\n",
- __func__, strerror(errno));
- _exit(1);
- }
- if (vpid == -1) // done
- break;
- v = '0';
- cred.pid = vpid;
- if (send_creds(sock, &cred, v, true) != SEND_CREDS_OK) {
- v = '1';
- cred.pid = getpid();
- if (send_creds(sock, &cred, v, false) != SEND_CREDS_OK)
- _exit(1);
- }
- }
- _exit(0);
-}
-
-static void pid_from_ns_wrapper(int sock, pid_t tpid)
-{
- int newnsfd = -1, ret, cpipe[2];
- char fnam[100];
- pid_t cpid;
- fd_set s;
- struct timeval tv;
- char v;
-
- ret = snprintf(fnam, sizeof(fnam), "/proc/%d/ns/pid", tpid);
- if (ret < 0 || ret >= sizeof(fnam))
- _exit(1);
- newnsfd = open(fnam, O_RDONLY);
- if (newnsfd < 0)
- _exit(1);
- if (setns(newnsfd, 0) < 0)
- _exit(1);
- close(newnsfd);
-
- if (pipe(cpipe) < 0)
- _exit(1);
-
-loop:
- cpid = fork();
-
- if (cpid < 0)
- _exit(1);
-
- if (!cpid) {
- char b = '1';
- close(cpipe[0]);
- if (write(cpipe[1], &b, sizeof(char)) < 0) {
- fprintf(stderr, "%s (child): erorr on write: %s\n",
- __func__, strerror(errno));
- }
- close(cpipe[1]);
- pid_from_ns(sock, tpid);
- }
-
- // give the child 1 second to be done forking and
- // write it's ack
- FD_ZERO(&s);
- FD_SET(cpipe[0], &s);
- tv.tv_sec = 1;
- tv.tv_usec = 0;
- ret = select(cpipe[0]+1, &s, NULL, NULL, &tv);
- if (ret <= 0)
- goto again;
- ret = read(cpipe[0], &v, 1);
- if (ret != sizeof(char) || v != '1') {
- goto again;
- }
-
- if (!wait_for_pid(cpid))
- _exit(1);
- _exit(0);
-
-again:
- kill(cpid, SIGKILL);
- wait_for_pid(cpid);
- goto loop;
-}
-
-static bool do_write_pids(pid_t tpid, const char *contrl, const char *cg, const char *file, const char *buf)
-{
- int sock[2] = {-1, -1};
- pid_t qpid, cpid = -1;
- bool answer = false, fail = false;
-
- /*
- * write the pids to a socket, have helper in writer's pidns
- * call movepid for us
- */
- if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sock) < 0) {
- perror("socketpair");
- exit(1);
- }
-
- cpid = fork();
- if (cpid == -1)
- goto out;
-
- if (!cpid) // child
- pid_from_ns_wrapper(sock[1], tpid);
-
- const char *ptr = buf;
- while (sscanf(ptr, "%d", &qpid) == 1) {
- struct ucred cred;
- char v;
-
- if (write(sock[0], &qpid, sizeof(qpid)) != sizeof(qpid)) {
- fprintf(stderr, "%s: error writing pid to child: %s\n",
- __func__, strerror(errno));
- goto out;
- }
-
- if (recv_creds(sock[0], &cred, &v)) {
- if (v == '0') {
- if (!cgm_move_pid(contrl, cg, cred.pid))
- fail = true;
- }
- }
-
- ptr = strchr(ptr, '\n');
- if (!ptr)
- break;
- ptr++;
- }
-
- /* All good, write the value */
- qpid = -1;
- if (write(sock[0], &qpid ,sizeof(qpid)) != sizeof(qpid))
- fprintf(stderr, "Warning: failed to ask child to exit\n");
-
- if (!fail)
- answer = true;
-
-out:
- if (cpid != -1)
- wait_for_pid(cpid);
- if (sock[0] != -1) {
- close(sock[0]);
- close(sock[1]);
- }
- return answer;
-}
-
-int cg_write(const char *path, const char *buf, size_t size, off_t offset,
- struct fuse_file_info *fi)
-{
- struct fuse_context *fc = fuse_get_context();
- char *localbuf = NULL;
- struct cgm_keys *k = NULL;
- struct file_info *f = (struct file_info *)fi->fh;
- bool r;
-
- if (f->type != LXC_TYPE_CGFILE) {
- fprintf(stderr, "Internal error: directory cache info used in cg_write\n");
- return -EIO;
- }
-
- if (offset)
- return 0;
-
- if (!fc)
- return -EIO;
-
- localbuf = alloca(size+1);
- localbuf[size] = '\0';
- memcpy(localbuf, buf, size);
-
- if ((k = get_cgroup_key(f->controller, f->cgroup, f->file)) == NULL) {
- size = -EINVAL;
- goto out;
- }
-
- if (!fc_may_access(fc, f->controller, f->cgroup, f->file, O_WRONLY)) {
- size = -EACCES;
- goto out;
- }
-
- if (strcmp(f->file, "tasks") == 0 ||
- strcmp(f->file, "/tasks") == 0 ||
- strcmp(f->file, "/cgroup.procs") == 0 ||
- strcmp(f->file, "cgroup.procs") == 0)
- // special case - we have to translate the pids
- r = do_write_pids(fc->pid, f->controller, f->cgroup, f->file, localbuf);
- else
- r = cgm_set_value(f->controller, f->cgroup, f->file, localbuf);
-
- if (!r)
- size = -EINVAL;
-
-out:
- free_key(k);
- return size;
-}
-
-int cg_chown(const char *path, uid_t uid, gid_t gid)
-{
- struct fuse_context *fc = fuse_get_context();
- char *cgdir = NULL, *fpath = NULL, *path1, *path2, *controller;
- struct cgm_keys *k = NULL;
- const char *cgroup;
- int ret;
-
- if (!fc)
- return -EIO;
-
- if (strcmp(path, "/cgroup") == 0)
- return -EINVAL;
-
- controller = pick_controller_from_path(fc, path);
- if (!controller)
- return -EINVAL;
- cgroup = find_cgroup_in_path(path);
- if (!cgroup)
- /* this is just /cgroup/controller */
- return -EINVAL;
-
- get_cgdir_and_path(cgroup, &cgdir, &fpath);
-
- if (!fpath) {
- path1 = "/";
- path2 = cgdir;
- } else {
- path1 = cgdir;
- path2 = fpath;
- }
-
- if (is_child_cgroup(controller, path1, path2)) {
- // get uid, gid, from '/tasks' file and make up a mode
- // That is a hack, until cgmanager gains a GetCgroupPerms fn.
- k = get_cgroup_key(controller, cgroup, "tasks");
-
- } else
- k = get_cgroup_key(controller, path1, path2);
-
- if (!k) {
- ret = -EINVAL;
- goto out;
- }
-
- /*
- * This being a fuse request, the uid and gid must be valid
- * in the caller's namespace. So we can just check to make
- * sure that the caller is root in his uid, and privileged
- * over the file's current owner.
- */
- if (!is_privileged_over(fc->pid, fc->uid, k->uid, NS_ROOT_REQD)) {
- ret = -EACCES;
- goto out;
- }
-
- if (!cgm_chown_file(controller, cgroup, uid, gid)) {
- ret = -EINVAL;
- goto out;
- }
-
- ret = 0;
-
-out:
- free_key(k);
- free(cgdir);
-
- return ret;
-}
-
-int cg_chmod(const char *path, mode_t mode)
-{
- struct fuse_context *fc = fuse_get_context();
- char * cgdir = NULL, *fpath = NULL, *path1, *path2, *controller;
- struct cgm_keys *k = NULL;
- const char *cgroup;
- int ret;
-
- if (!fc)
- return -EIO;
-
- if (strcmp(path, "/cgroup") == 0)
- return -EINVAL;
-
- controller = pick_controller_from_path(fc, path);
- if (!controller)
- return -EINVAL;
- cgroup = find_cgroup_in_path(path);
- if (!cgroup)
- /* this is just /cgroup/controller */
- return -EINVAL;
-
- get_cgdir_and_path(cgroup, &cgdir, &fpath);
-
- if (!fpath) {
- path1 = "/";
- path2 = cgdir;
- } else {
- path1 = cgdir;
- path2 = fpath;
- }
-
- if (is_child_cgroup(controller, path1, path2)) {
- // get uid, gid, from '/tasks' file and make up a mode
- // That is a hack, until cgmanager gains a GetCgroupPerms fn.
- k = get_cgroup_key(controller, cgroup, "tasks");
-
- } else
- k = get_cgroup_key(controller, path1, path2);
-
- if (!k) {
- ret = -EINVAL;
- goto out;
- }
-
- /*
- * This being a fuse request, the uid and gid must be valid
- * in the caller's namespace. So we can just check to make
- * sure that the caller is root in his uid, and privileged
- * over the file's current owner.
- */
- if (!is_privileged_over(fc->pid, fc->uid, k->uid, NS_ROOT_OPT)) {
- ret = -EPERM;
- goto out;
- }
-
- if (!cgm_chmod_file(controller, cgroup, mode)) {
- ret = -EINVAL;
- goto out;
- }
-
- ret = 0;
-out:
- free_key(k);
- free(cgdir);
- return ret;
-}
-
-#define LXCFS_MKDIR_PATH LIBEXECDIR "/lxcfs/lxcfs_mkdir"
-
-int cg_mkdir(const char *path, mode_t mode)
-{
- struct fuse_context *fc = fuse_get_context();
- char *fpath = NULL, *path1, *cgdir = NULL, *controller;
- const char *cgroup;
- int ret;
-
- if (!fc)
- return -EIO;
-
-
- controller = pick_controller_from_path(fc, path);
- if (!controller)
- return -EINVAL;
-
- cgroup = find_cgroup_in_path(path);
- if (!cgroup)
- return -EINVAL;
-
- get_cgdir_and_path(cgroup, &cgdir, &fpath);
- if (!fpath)
- path1 = "/";
- else
- path1 = cgdir;
-
- if (!fc_may_access(fc, controller, path1, NULL, O_RDWR)) {
- ret = -EACCES;
- goto out;
- }
- if (!caller_is_in_ancestor(fc->pid, controller, path1, NULL)) {
- ret = -EACCES;
- goto out;
- }
-
- if (fc->uid == 0 && fc->gid == 0) {
- if (!cgm_create(controller, cgroup)) {
- ret = -EINVAL;
- goto out;
- }
- } else {
- /*
- * exec a helper so as to get a clean dbus connection
- * 17 for lxcfs_mkdir, and spaces and newline and \0. 50 for two ints.
- * 50 for two ints
- */
- size_t len = strlen(cgroup) + strlen(controller) + 17 + 50;
- char *cmd = alloca(len);
- ret = snprintf(cmd, len, "%s %d %d %s %s\n", LXCFS_MKDIR_PATH,
- fc->uid, fc->gid, controller, cgroup);
- if (ret < 0 || ret >= len) {
- ret = -EINVAL;
- goto out;
- }
- ret = system(cmd);
- if (ret != 0)
- goto out;
- }
-
- ret = 0;
-
-out:
- free(cgdir);
- return ret;
-}
-
-static int cg_rmdir(const char *path)
-{
- struct fuse_context *fc = fuse_get_context();
- char *fpath = NULL, *cgdir = NULL, *controller;
- const char *cgroup;
- int ret;
-
- if (!fc)
- return -EIO;
-
- controller = pick_controller_from_path(fc, path);
- if (!controller)
- return -EINVAL;
-
- cgroup = find_cgroup_in_path(path);
- if (!cgroup)
- return -EINVAL;
-
- get_cgdir_and_path(cgroup, &cgdir, &fpath);
- if (!fpath) {
- ret = -EINVAL;
- goto out;
- }
-
- fprintf(stderr, "rmdir: verifying access to %s:%s (req path %s)\n",
- controller, cgdir, path);
- if (!fc_may_access(fc, controller, cgdir, NULL, O_WRONLY)) {
- ret = -EACCES;
- goto out;
- }
- if (!caller_is_in_ancestor(fc->pid, controller, cgroup, NULL)) {
- ret = -EACCES;
- goto out;
- }
-
- if (!cgm_remove(controller, cgroup)) {
- ret = -EINVAL;
- goto out;
- }
-
- ret = 0;
-
-out:
- free(cgdir);
- return ret;
-}
-
-static bool startswith(const char *line, const char *pref)
-{
- if (strncmp(line, pref, strlen(pref)) == 0)
- return true;
- return false;
-}
-
-static void get_mem_cached(char *memstat, unsigned long *v)
-{
- char *eol;
-
- *v = 0;
- while (*memstat) {
- if (startswith(memstat, "total_cache")) {
- sscanf(memstat + 11, "%lu", v);
- *v /= 1024;
- return;
- }
- eol = strchr(memstat, '\n');
- if (!eol)
- return;
- memstat = eol+1;
- }
-}
-
-static void get_blkio_io_value(char *str, unsigned major, unsigned minor, char *iotype, unsigned long *v)
-{
- char *eol;
- char key[32];
-
- memset(key, 0, 32);
- snprintf(key, 32, "%u:%u %s", major, minor, iotype);
-
- size_t len = strlen(key);
- *v = 0;
-
- while (*str) {
- if (startswith(str, key)) {
- sscanf(str + len, "%lu", v);
- return;
- }
- eol = strchr(str, '\n');
- if (!eol)
- return;
- str = eol+1;
- }
-}
-
-static int read_file(const char *path, char *buf, size_t size,
- struct file_info *d)
-{
- size_t linelen = 0, total_len = 0, rv = 0;
- char *line = NULL;
- char *cache = d->buf;
- size_t cache_size = d->buflen;
- FILE *f = fopen(path, "r");
- if (!f)
- return 0;
-
- while (getline(&line, &linelen, f) != -1) {
- size_t l = snprintf(cache, cache_size, "%s", line);
- if (l < 0) {
- perror("Error writing to cache");
- rv = 0;
- goto err;
- }
- if (l >= cache_size) {
- fprintf(stderr, "Internal error: truncated write to cache\n");
- rv = 0;
- goto err;
- }
- if (l < cache_size) {
- cache += l;
- cache_size -= l;
- total_len += l;
- } else {
- cache += cache_size;
- total_len += cache_size;
- cache_size = 0;
- break;
- }
- }
-
- d->size = total_len;
- if (total_len > size ) total_len = size;
-
- /* read from off 0 */
- memcpy(buf, d->buf, total_len);
- rv = total_len;
- err:
- fclose(f);
- free(line);
- return rv;
-}
-
-/*
- * FUSE ops for /proc
- */
-
-static int proc_meminfo_read(char *buf, size_t size, off_t offset,
- struct fuse_file_info *fi)
-{
- struct fuse_context *fc = fuse_get_context();
- struct file_info *d = (struct file_info *)fi->fh;
- char *cg;
- char *memlimit_str = NULL, *memusage_str = NULL, *memstat_str = NULL;
- unsigned long memlimit = 0, memusage = 0, cached = 0, hosttotal = 0;
- char *line = NULL;
- size_t linelen = 0, total_len = 0, rv = 0;
- char *cache = d->buf;
- size_t cache_size = d->buflen;
- FILE *f = NULL;
-
- if (offset){
- if (offset > d->size)
- return -EINVAL;
- if (!d->cached)
- return 0;
- int left = d->size - offset;
- total_len = left > size ? size: left;
- memcpy(buf, cache + offset, total_len);
- return total_len;
- }
-
- cg = get_pid_cgroup(fc->pid, "memory");
- if (!cg)
- return read_file("/proc/meminfo", buf, size, d);
-
- if (!cgm_get_value("memory", cg, "memory.limit_in_bytes", &memlimit_str))
- goto err;
- if (!cgm_get_value("memory", cg, "memory.usage_in_bytes", &memusage_str))
- goto err;
- if (!cgm_get_value("memory", cg, "memory.stat", &memstat_str))
- goto err;
- memlimit = strtoul(memlimit_str, NULL, 10);
- memusage = strtoul(memusage_str, NULL, 10);
- memlimit /= 1024;
- memusage /= 1024;
- get_mem_cached(memstat_str, &cached);
-
- f = fopen("/proc/meminfo", "r");
- if (!f)
- goto err;
-
- while (getline(&line, &linelen, f) != -1) {
- size_t l;
- char *printme, lbuf[100];
-
- memset(lbuf, 0, 100);
- if (startswith(line, "MemTotal:")) {
- sscanf(line+14, "%lu", &hosttotal);
- if (hosttotal < memlimit)
- memlimit = hosttotal;
- snprintf(lbuf, 100, "MemTotal: %8lu kB\n", memlimit);
- printme = lbuf;
- } else if (startswith(line, "MemFree:")) {
- snprintf(lbuf, 100, "MemFree: %8lu kB\n", memlimit - memusage);
- printme = lbuf;
- } else if (startswith(line, "MemAvailable:")) {
- snprintf(lbuf, 100, "MemAvailable: %8lu kB\n", memlimit - memusage);
- printme = lbuf;
- } else if (startswith(line, "Buffers:")) {
- snprintf(lbuf, 100, "Buffers: %8lu kB\n", 0UL);
- printme = lbuf;
- } else if (startswith(line, "Cached:")) {
- snprintf(lbuf, 100, "Cached: %8lu kB\n", cached);
- printme = lbuf;
- } else if (startswith(line, "SwapCached:")) {
- snprintf(lbuf, 100, "SwapCached: %8lu kB\n", 0UL);
- printme = lbuf;
- } else
- printme = line;
-
- l = snprintf(cache, cache_size, "%s", printme);
- if (l < 0) {
- perror("Error writing to cache");
- rv = 0;
- goto err;
-
- }
- if (l >= cache_size) {
- fprintf(stderr, "Internal error: truncated write to cache\n");
- rv = 0;
- goto err;
- }
-
- cache += l;
- cache_size -= l;
- total_len += l;
- }
-
- d->cached = 1;
- d->size = total_len;
- if (total_len > size ) total_len = size;
- memcpy(buf, d->buf, total_len);
-
- rv = total_len;
-err:
- if (f)
- fclose(f);
- free(line);
- free(cg);
- free(memlimit_str);
- free(memusage_str);
- free(memstat_str);
- return rv;
-}
-
-/*
- * Read the cpuset.cpus for cg
- * Return the answer in a newly allocated string which must be freed
- */
-static char *get_cpuset(const char *cg)
-{
- char *answer;
-
- if (!cgm_get_value("cpuset", cg, "cpuset.cpus", &answer))
- return NULL;
- return answer;
-}
-
-bool cpu_in_cpuset(int cpu, const char *cpuset);
-
-static bool cpuline_in_cpuset(const char *line, const char *cpuset)
-{
- int cpu;
-
- if (sscanf(line, "processor : %d", &cpu) != 1)
- return false;
- return cpu_in_cpuset(cpu, cpuset);
-}
-
-/*
- * check whether this is a '^processor" line in /proc/cpuinfo
- */
-static bool is_processor_line(const char *line)
-{
- int cpu;
-
- if (sscanf(line, "processor : %d", &cpu) == 1)
- return true;
- return false;
-}
-
-static int proc_cpuinfo_read(char *buf, size_t size, off_t offset,
- struct fuse_file_info *fi)
-{
- struct fuse_context *fc = fuse_get_context();
- struct file_info *d = (struct file_info *)fi->fh;
- char *cg;
- char *cpuset = NULL;
- char *line = NULL;
- size_t linelen = 0, total_len = 0, rv = 0;
- bool am_printing = false;
- int curcpu = -1;
- char *cache = d->buf;
- size_t cache_size = d->buflen;
- FILE *f = NULL;
-
- if (offset){
- if (offset > d->size)
- return -EINVAL;
- if (!d->cached)
- return 0;
- int left = d->size - offset;
- total_len = left > size ? size: left;
- memcpy(buf, cache + offset, total_len);
- return total_len;
- }
-
- cg = get_pid_cgroup(fc->pid, "cpuset");
- if (!cg)
- return read_file("proc/cpuinfo", buf, size, d);
-
- cpuset = get_cpuset(cg);
- if (!cpuset)
- goto err;
-
- f = fopen("/proc/cpuinfo", "r");
- if (!f)
- goto err;
-
- while (getline(&line, &linelen, f) != -1) {
- size_t l;
- if (is_processor_line(line)) {
- am_printing = cpuline_in_cpuset(line, cpuset);
- if (am_printing) {
- curcpu ++;
- l = snprintf(cache, cache_size, "processor : %d\n", curcpu);
- if (l < 0) {
- perror("Error writing to cache");
- rv = 0;
- goto err;
- }
- if (l >= cache_size) {
- fprintf(stderr, "Internal error: truncated write to cache\n");
- rv = 0;
- goto err;
- }
- if (l < cache_size){
- cache += l;
- cache_size -= l;
- total_len += l;
- }else{
- cache += cache_size;
- total_len += cache_size;
- cache_size = 0;
- break;
- }
- }
- continue;
- }
- if (am_printing) {
- l = snprintf(cache, cache_size, "%s", line);
- if (l < 0) {
- perror("Error writing to cache");
- rv = 0;
- goto err;
- }
- if (l >= cache_size) {
- fprintf(stderr, "Internal error: truncated write to cache\n");
- rv = 0;
- goto err;
- }
- if (l < cache_size) {
- cache += l;
- cache_size -= l;
- total_len += l;
- } else {
- cache += cache_size;
- total_len += cache_size;
- cache_size = 0;
- break;
- }
- }
- }
-
- d->cached = 1;
- d->size = total_len;
- if (total_len > size ) total_len = size;
-
- /* read from off 0 */
- memcpy(buf, d->buf, total_len);
- rv = total_len;
-err:
- if (f)
- fclose(f);
- free(line);
- free(cpuset);
- free(cg);
- return rv;
-}
-
-static int proc_stat_read(char *buf, size_t size, off_t offset,
- struct fuse_file_info *fi)
-{
- struct fuse_context *fc = fuse_get_context();
- struct file_info *d = (struct file_info *)fi->fh;
- char *cg;
- char *cpuset = NULL;
- char *line = NULL;
- size_t linelen = 0, total_len = 0, rv = 0;
- int curcpu = -1; /* cpu numbering starts at 0 */
- unsigned long user = 0, nice = 0, system = 0, idle = 0, iowait = 0, irq = 0, softirq = 0, steal = 0, guest = 0;
- unsigned long user_sum = 0, nice_sum = 0, system_sum = 0, idle_sum = 0, iowait_sum = 0,
- irq_sum = 0, softirq_sum = 0, steal_sum = 0, guest_sum = 0;
-#define CPUALL_MAX_SIZE BUF_RESERVE_SIZE
- char cpuall[CPUALL_MAX_SIZE];
- /* reserve for cpu all */
- char *cache = d->buf + CPUALL_MAX_SIZE;
- size_t cache_size = d->buflen - CPUALL_MAX_SIZE;
- FILE *f = NULL;
-
- if (offset){
- if (offset > d->size)
- return -EINVAL;
- if (!d->cached)
- return 0;
- int left = d->size - offset;
- total_len = left > size ? size: left;
- memcpy(buf, d->buf + offset, total_len);
- return total_len;
- }
-
- cg = get_pid_cgroup(fc->pid, "cpuset");
- if (!cg)
- return read_file("/proc/stat", buf, size, d);
-
- cpuset = get_cpuset(cg);
- if (!cpuset)
- goto err;
-
- f = fopen("/proc/stat", "r");
- if (!f)
- goto err;
-
- //skip first line
- if (getline(&line, &linelen, f) < 0) {
- fprintf(stderr, "proc_stat_read read first line failed\n");
- goto err;
- }
-
- while (getline(&line, &linelen, f) != -1) {
- size_t l;
- int cpu;
- char cpu_char[10]; /* That's a lot of cores */
- char *c;
-
- if (sscanf(line, "cpu%9[^ ]", cpu_char) != 1) {
- /* not a ^cpuN line containing a number N, just print it */
- l = snprintf(cache, cache_size, "%s", line);
- if (l < 0) {
- perror("Error writing to cache");
- rv = 0;
- goto err;
- }
- if (l >= cache_size) {
- fprintf(stderr, "Internal error: truncated write to cache\n");
- rv = 0;
- goto err;
- }
- if (l < cache_size) {
- cache += l;
- cache_size -= l;
- total_len += l;
- continue;
- } else {
- //no more space, break it
- cache += cache_size;
- total_len += cache_size;
- cache_size = 0;
- break;
- }
- }
-
- if (sscanf(cpu_char, "%d", &cpu) != 1)
- continue;
- if (!cpu_in_cpuset(cpu, cpuset))
- continue;
- curcpu ++;
-
- c = strchr(line, ' ');
- if (!c)
- continue;
- l = snprintf(cache, cache_size, "cpu%d%s", curcpu, c);
- if (l < 0) {
- perror("Error writing to cache");
- rv = 0;
- goto err;