]> git.proxmox.com Git - mirror_lxcfs.git/blame - bindings.c
configure.ac: add -Wno-portability
[mirror_lxcfs.git] / bindings.c
CommitLineData
237e200e
SH
1/* lxcfs
2 *
3 * Copyright © 2014-2016 Canonical, Inc
4 * Author: Serge Hallyn <serge.hallyn@ubuntu.com>
5 *
6 * See COPYING file for details.
7 */
8
9#define FUSE_USE_VERSION 26
10
11#include <stdio.h>
12#include <dirent.h>
13#include <fcntl.h>
14#include <fuse.h>
15#include <unistd.h>
16#include <errno.h>
17#include <stdbool.h>
18#include <time.h>
19#include <string.h>
20#include <stdlib.h>
21#include <libgen.h>
22#include <sched.h>
23#include <pthread.h>
24#include <linux/sched.h>
25#include <sys/param.h>
26#include <sys/socket.h>
27#include <sys/mount.h>
28#include <sys/epoll.h>
29#include <wait.h>
30
237e200e
SH
31#include "bindings.h"
32
33#include "config.h" // for VERSION
34
35enum {
36 LXC_TYPE_CGDIR,
37 LXC_TYPE_CGFILE,
38 LXC_TYPE_PROC_MEMINFO,
39 LXC_TYPE_PROC_CPUINFO,
40 LXC_TYPE_PROC_UPTIME,
41 LXC_TYPE_PROC_STAT,
42 LXC_TYPE_PROC_DISKSTATS,
43};
44
45struct file_info {
46 char *controller;
47 char *cgroup;
48 char *file;
49 int type;
50 char *buf; // unused as of yet
51 int buflen;
52 int size; //actual data size
53 int cached;
54};
55
56/* reserve buffer size, for cpuall in /proc/stat */
57#define BUF_RESERVE_SIZE 256
58
59/*
60 * A table caching which pid is init for a pid namespace.
61 * When looking up which pid is init for $qpid, we first
62 * 1. Stat /proc/$qpid/ns/pid.
63 * 2. Check whether the ino_t is in our store.
64 * a. if not, fork a child in qpid's ns to send us
65 * ucred.pid = 1, and read the initpid. Cache
66 * initpid and creation time for /proc/initpid
67 * in a new store entry.
68 * b. if so, verify that /proc/initpid still matches
69 * what we have saved. If not, clear the store
70 * entry and go back to a. If so, return the
71 * cached initpid.
72 */
73struct pidns_init_store {
74 ino_t ino; // inode number for /proc/$pid/ns/pid
75 pid_t initpid; // the pid of nit in that ns
76 long int ctime; // the time at which /proc/$initpid was created
77 struct pidns_init_store *next;
78 long int lastcheck;
79};
80
81/* lol - look at how they are allocated in the kernel */
82#define PIDNS_HASH_SIZE 4096
83#define HASH(x) ((x) % PIDNS_HASH_SIZE)
84
85static struct pidns_init_store *pidns_hash_table[PIDNS_HASH_SIZE];
86static pthread_mutex_t pidns_store_mutex = PTHREAD_MUTEX_INITIALIZER;
87static void lock_mutex(pthread_mutex_t *l)
88{
89 int ret;
90
91 if ((ret = pthread_mutex_lock(l)) != 0) {
92 fprintf(stderr, "pthread_mutex_lock returned:%d %s\n", ret, strerror(ret));
93 exit(1);
94 }
95}
96
97static void unlock_mutex(pthread_mutex_t *l)
98{
99 int ret;
100
101 if ((ret = pthread_mutex_unlock(l)) != 0) {
102 fprintf(stderr, "pthread_mutex_unlock returned:%d %s\n", ret, strerror(ret));
103 exit(1);
104 }
105}
106
107static void store_lock(void)
108{
109 lock_mutex(&pidns_store_mutex);
110}
111
112static void store_unlock(void)
113{
114 unlock_mutex(&pidns_store_mutex);
115}
116
117/* Must be called under store_lock */
118static bool initpid_still_valid(struct pidns_init_store *e, struct stat *nsfdsb)
119{
120 struct stat initsb;
121 char fnam[100];
122
123 snprintf(fnam, 100, "/proc/%d", e->initpid);
124 if (stat(fnam, &initsb) < 0)
125 return false;
126#if DEBUG
127 fprintf(stderr, "comparing ctime %ld %ld for pid %d\n",
128 e->ctime, initsb.st_ctime, e->initpid);
129#endif
130 if (e->ctime != initsb.st_ctime)
131 return false;
132 return true;
133}
134
135/* Must be called under store_lock */
136static void remove_initpid(struct pidns_init_store *e)
137{
138 struct pidns_init_store *tmp;
139 int h;
140
141#if DEBUG
142 fprintf(stderr, "remove_initpid: removing entry for %d\n", e->initpid);
143#endif
144 h = HASH(e->ino);
145 if (pidns_hash_table[h] == e) {
146 pidns_hash_table[h] = e->next;
147 free(e);
148 return;
149 }
150
151 tmp = pidns_hash_table[h];
152 while (tmp) {
153 if (tmp->next == e) {
154 tmp->next = e->next;
155 free(e);
156 return;
157 }
158 tmp = tmp->next;
159 }
160}
161
162#define PURGE_SECS 5
163/* Must be called under store_lock */
164static void prune_initpid_store(void)
165{
166 static long int last_prune = 0;
167 struct pidns_init_store *e, *prev, *delme;
168 long int now, threshold;
169 int i;
170
171 if (!last_prune) {
172 last_prune = time(NULL);
173 return;
174 }
175 now = time(NULL);
176 if (now < last_prune + PURGE_SECS)
177 return;
178#if DEBUG
179 fprintf(stderr, "pruning\n");
180#endif
181 last_prune = now;
182 threshold = now - 2 * PURGE_SECS;
183
184 for (i = 0; i < PIDNS_HASH_SIZE; i++) {
185 for (prev = NULL, e = pidns_hash_table[i]; e; ) {
186 if (e->lastcheck < threshold) {
187#if DEBUG
188 fprintf(stderr, "Removing cached entry for %d\n", e->initpid);
189#endif
190 delme = e;
191 if (prev)
192 prev->next = e->next;
193 else
194 pidns_hash_table[i] = e->next;
195 e = e->next;
196 free(delme);
197 } else {
198 prev = e;
199 e = e->next;
200 }
201 }
202 }
203}
204
205/* Must be called under store_lock */
206static void save_initpid(struct stat *sb, pid_t pid)
207{
208 struct pidns_init_store *e;
209 char fpath[100];
210 struct stat procsb;
211 int h;
212
213#if DEBUG
214 fprintf(stderr, "save_initpid: adding entry for %d\n", pid);
215#endif
216 snprintf(fpath, 100, "/proc/%d", pid);
217 if (stat(fpath, &procsb) < 0)
218 return;
219 do {
220 e = malloc(sizeof(*e));
221 } while (!e);
222 e->ino = sb->st_ino;
223 e->initpid = pid;
224 e->ctime = procsb.st_ctime;
225 h = HASH(e->ino);
226 e->next = pidns_hash_table[h];
227 e->lastcheck = time(NULL);
228 pidns_hash_table[h] = e;
229}
230
231/*
232 * Given the stat(2) info for a nsfd pid inode, lookup the init_pid_store
233 * entry for the inode number and creation time. Verify that the init pid
234 * is still valid. If not, remove it. Return the entry if valid, NULL
235 * otherwise.
236 * Must be called under store_lock
237 */
238static struct pidns_init_store *lookup_verify_initpid(struct stat *sb)
239{
240 int h = HASH(sb->st_ino);
241 struct pidns_init_store *e = pidns_hash_table[h];
242
243 while (e) {
244 if (e->ino == sb->st_ino) {
245 if (initpid_still_valid(e, sb)) {
246 e->lastcheck = time(NULL);
247 return e;
248 }
249 remove_initpid(e);
250 return NULL;
251 }
252 e = e->next;
253 }
254
255 return NULL;
256}
257
258static int is_dir(const char *path)
259{
260 struct stat statbuf;
261 int ret = stat(path, &statbuf);
262 if (ret == 0 && S_ISDIR(statbuf.st_mode))
263 return 1;
264 return 0;
265}
266
267static char *must_copy_string(const char *str)
268{
269 char *dup = NULL;
270 if (!str)
271 return NULL;
272 do {
273 dup = strdup(str);
274 } while (!dup);
275
276 return dup;
277}
278
279static inline void drop_trailing_newlines(char *s)
280{
281 int l;
282
283 for (l=strlen(s); l>0 && s[l-1] == '\n'; l--)
284 s[l-1] = '\0';
285}
286
287#define BATCH_SIZE 50
288static void dorealloc(char **mem, size_t oldlen, size_t newlen)
289{
290 int newbatches = (newlen / BATCH_SIZE) + 1;
291 int oldbatches = (oldlen / BATCH_SIZE) + 1;
292
293 if (!*mem || newbatches > oldbatches) {
294 char *tmp;
295 do {
296 tmp = realloc(*mem, newbatches * BATCH_SIZE);
297 } while (!tmp);
298 *mem = tmp;
299 }
300}
301static void append_line(char **contents, size_t *len, char *line, ssize_t linelen)
302{
303 size_t newlen = *len + linelen;
304 dorealloc(contents, *len, newlen + 1);
305 memcpy(*contents + *len, line, linelen+1);
306 *len = newlen;
307}
308
309static char *slurp_file(const char *from)
310{
311 char *line = NULL;
312 char *contents = NULL;
313 FILE *f = fopen(from, "r");
314 size_t len = 0, fulllen = 0;
315 ssize_t linelen;
316
317 if (!f)
318 return NULL;
319
320 while ((linelen = getline(&line, &len, f)) != -1) {
321 append_line(&contents, &fulllen, line, linelen);
322 }
323 fclose(f);
324
325 if (contents)
326 drop_trailing_newlines(contents);
327 free(line);
328 return contents;
329}
330
331static bool write_string(const char *fnam, const char *string)
332{
333 FILE *f;
334 size_t len, ret;
335
336 if (!(f = fopen(fnam, "w")))
337 return false;
338 len = strlen(string);
339 ret = fwrite(string, 1, len, f);
340 if (ret != len) {
341 fprintf(stderr, "Error writing to file: %s\n", strerror(errno));
342 fclose(f);
343 return false;
344 }
345 if (fclose(f) < 0) {
346 fprintf(stderr, "Error writing to file: %s\n", strerror(errno));
347 return false;
348 }
349 return true;
350}
351
352/*
353 * hierarchies, i.e. 'cpu,cpuacct'
354 */
355char **hierarchies;
356int num_hierarchies;
357
358struct cgfs_files {
359 char *name;
360 uint32_t uid, gid;
361 uint32_t mode;
362};
363
0619767c 364#define ALLOC_NUM 20
237e200e
SH
365static bool store_hierarchy(char *stridx, char *h)
366{
0619767c
SH
367 if (num_hierarchies % ALLOC_NUM == 0) {
368 size_t n = (num_hierarchies / ALLOC_NUM) + 1;
369 n *= ALLOC_NUM;
370 char **tmp = realloc(hierarchies, n * sizeof(char *));
0619767c
SH
371 if (!tmp) {
372 fprintf(stderr, "Out of memory\n");
373 exit(1);
374 }
237e200e 375 hierarchies = tmp;
237e200e
SH
376 }
377
0619767c 378 hierarchies[num_hierarchies++] = must_copy_string(h);
237e200e
SH
379 return true;
380}
381
382static void print_subsystems(void)
383{
384 int i;
385
386 fprintf(stderr, "hierarchies:");
387 for (i = 0; i < num_hierarchies; i++) {
388 if (hierarchies[i])
389 fprintf(stderr, " %d: %s\n", i, hierarchies[i]);
390 }
391}
392
393static bool in_comma_list(const char *needle, const char *haystack)
394{
395 const char *s = haystack, *e;
396 size_t nlen = strlen(needle);
397
398 while (*s && (e = index(s, ','))) {
399 if (nlen != e - s) {
400 s = e + 1;
401 continue;
402 }
403 if (strncmp(needle, s, nlen) == 0)
404 return true;
405 s = e + 1;
406 }
407 if (strcmp(needle, s) == 0)
408 return true;
409 return false;
410}
411
412/* do we need to do any massaging here? I'm not sure... */
413static char *find_mounted_controller(const char *controller)
414{
415 int i;
416
417 for (i = 0; i < num_hierarchies; i++) {
418 if (!hierarchies[i])
419 continue;
420 if (strcmp(hierarchies[i], controller) == 0)
421 return hierarchies[i];
422 if (in_comma_list(controller, hierarchies[i]))
423 return hierarchies[i];
424 }
425
426 return NULL;
427}
428
429bool cgfs_set_value(const char *controller, const char *cgroup, const char *file,
430 const char *value)
431{
432 size_t len;
433 char *fnam, *tmpc = find_mounted_controller(controller);
434
435 if (!tmpc)
436 return false;
437 /* basedir / tmpc / cgroup / file \0 */
438 len = strlen(basedir) + strlen(tmpc) + strlen(cgroup) + strlen(file) + 4;
439 fnam = alloca(len);
440 snprintf(fnam, len, "%s/%s/%s/%s", basedir, tmpc, cgroup, file);
441
442 return write_string(fnam, value);
443}
444
445// Chown all the files in the cgroup directory. We do this when we create
446// a cgroup on behalf of a user.
447static void chown_all_cgroup_files(const char *dirname, uid_t uid, gid_t gid)
448{
449 struct dirent dirent, *direntp;
450 char path[MAXPATHLEN];
451 size_t len;
452 DIR *d;
453 int ret;
454
455 len = strlen(dirname);
456 if (len >= MAXPATHLEN) {
457 fprintf(stderr, "chown_all_cgroup_files: pathname too long: %s\n", dirname);
458 return;
459 }
460
461 d = opendir(dirname);
462 if (!d) {
463 fprintf(stderr, "chown_all_cgroup_files: failed to open %s\n", dirname);
464 return;
465 }
466
467 while (readdir_r(d, &dirent, &direntp) == 0 && direntp) {
468 if (!strcmp(direntp->d_name, ".") || !strcmp(direntp->d_name, ".."))
469 continue;
470 ret = snprintf(path, MAXPATHLEN, "%s/%s", dirname, direntp->d_name);
471 if (ret < 0 || ret >= MAXPATHLEN) {
472 fprintf(stderr, "chown_all_cgroup_files: pathname too long under %s\n", dirname);
473 continue;
474 }
475 if (chown(path, uid, gid) < 0)
476 fprintf(stderr, "Failed to chown file %s to %u:%u", path, uid, gid);
477 }
478 closedir(d);
479}
480
481int cgfs_create(const char *controller, const char *cg, uid_t uid, gid_t gid)
482{
483 size_t len;
484 char *dirnam, *tmpc = find_mounted_controller(controller);
485
486 if (!tmpc)
487 return -EINVAL;
488 /* basedir / tmpc / cg \0 */
489 len = strlen(basedir) + strlen(tmpc) + strlen(cg) + 3;
490 dirnam = alloca(len);
491 snprintf(dirnam, len, "%s/%s/%s", basedir,tmpc, cg);
492
493 if (mkdir(dirnam, 0755) < 0)
494 return -errno;
495
496 if (uid == 0 && gid == 0)
497 return 0;
498
499 if (chown(dirnam, uid, gid) < 0)
500 return -errno;
501
502 chown_all_cgroup_files(dirnam, uid, gid);
503
504 return 0;
505}
506
507static bool recursive_rmdir(const char *dirname)
508{
509 struct dirent dirent, *direntp;
510 DIR *dir;
511 bool ret = false;
512 char pathname[MAXPATHLEN];
513
514 dir = opendir(dirname);
515 if (!dir) {
516#if DEBUG
517 fprintf(stderr, "%s: failed to open %s: %s\n", __func__, dirname, strerror(errno));
518#endif
519 return false;
520 }
521
522 while (!readdir_r(dir, &dirent, &direntp)) {
523 struct stat mystat;
524 int rc;
525
526 if (!direntp)
527 break;
528
529 if (!strcmp(direntp->d_name, ".") ||
530 !strcmp(direntp->d_name, ".."))
531 continue;
532
533 rc = snprintf(pathname, MAXPATHLEN, "%s/%s", dirname, direntp->d_name);
534 if (rc < 0 || rc >= MAXPATHLEN) {
535 fprintf(stderr, "pathname too long\n");
536 continue;
537 }
538
539 ret = lstat(pathname, &mystat);
540 if (ret) {
541#if DEBUG
542 fprintf(stderr, "%s: failed to stat %s: %s\n", __func__, pathname, strerror(errno));
543#endif
544 continue;
545 }
546 if (S_ISDIR(mystat.st_mode)) {
547 if (!recursive_rmdir(pathname)) {
548#if DEBUG
549 fprintf(stderr, "Error removing %s\n", pathname);
550#endif
551 }
552 }
553 }
554
555 ret = true;
556 if (closedir(dir) < 0) {
557 fprintf(stderr, "%s: failed to close directory %s: %s\n", __func__, dirname, strerror(errno));
558 ret = false;
559 }
560
561 if (rmdir(dirname) < 0) {
562#if DEBUG
563 fprintf(stderr, "%s: failed to delete %s: %s\n", __func__, dirname, strerror(errno));
564#endif
565 ret = false;
566 }
567
568 return ret;
569}
570
571bool cgfs_remove(const char *controller, const char *cg)
572{
573 size_t len;
574 char *dirnam, *tmpc = find_mounted_controller(controller);
575
576 if (!tmpc)
577 return false;
578 /* basedir / tmpc / cg \0 */
579 len = strlen(basedir) + strlen(tmpc) + strlen(cg) + 3;
580 dirnam = alloca(len);
581 snprintf(dirnam, len, "%s/%s/%s", basedir,tmpc, cg);
582 return recursive_rmdir(dirnam);
583}
584
585bool cgfs_chmod_file(const char *controller, const char *file, mode_t mode)
586{
587 size_t len;
588 char *pathname, *tmpc = find_mounted_controller(controller);
589
590 if (!tmpc)
591 return false;
592 /* basedir / tmpc / file \0 */
593 len = strlen(basedir) + strlen(tmpc) + strlen(file) + 3;
594 pathname = alloca(len);
595 snprintf(pathname, len, "%s/%s/%s", basedir, tmpc, file);
596 if (chmod(pathname, mode) < 0)
597 return false;
598 return true;
599}
600
601static int chown_tasks_files(const char *dirname, uid_t uid, gid_t gid)
602{
603 size_t len;
604 char *fname;
605
606 len = strlen(dirname) + strlen("/cgroup.procs") + 1;
607 fname = alloca(len);
608 snprintf(fname, len, "%s/tasks", dirname);
609 if (chown(fname, uid, gid) != 0)
610 return -errno;
611 snprintf(fname, len, "%s/cgroup.procs", dirname);
612 if (chown(fname, uid, gid) != 0)
613 return -errno;
614 return 0;
615}
616
617int cgfs_chown_file(const char *controller, const char *file, uid_t uid, gid_t gid)
618{
619 size_t len;
620 char *pathname, *tmpc = find_mounted_controller(controller);
621
622 if (!tmpc)
623 return -EINVAL;
624 /* basedir / tmpc / file \0 */
625 len = strlen(basedir) + strlen(tmpc) + strlen(file) + 3;
626 pathname = alloca(len);
627 snprintf(pathname, len, "%s/%s/%s", basedir, tmpc, file);
628 if (chown(pathname, uid, gid) < 0)
629 return -errno;
630
631 if (is_dir(pathname))
632 // like cgmanager did, we want to chown the tasks file as well
633 return chown_tasks_files(pathname, uid, gid);
634
635 return 0;
636}
637
638FILE *open_pids_file(const char *controller, const char *cgroup)
639{
640 size_t len;
641 char *pathname, *tmpc = find_mounted_controller(controller);
642
643 if (!tmpc)
644 return NULL;
645 /* basedir / tmpc / cgroup / "cgroup.procs" \0 */
646 len = strlen(basedir) + strlen(tmpc) + strlen(cgroup) + 4 + strlen("cgroup.procs");
647 pathname = alloca(len);
648 snprintf(pathname, len, "%s/%s/%s/cgroup.procs", basedir, tmpc, cgroup);
649 return fopen(pathname, "w");
650}
651
652bool cgfs_list_children(const char *controller, const char *cgroup, char ***list)
653{
654 size_t len;
655 char *dirname, *tmpc = find_mounted_controller(controller);
656 char pathname[MAXPATHLEN];
657 size_t sz = 0, asz = BATCH_SIZE;
658 struct dirent dirent, *direntp;
659 DIR *dir;
660 int ret;
661
662 do {
663 *list = malloc(asz * sizeof(char *));
664 } while (!*list);
665 (*list)[0] = NULL;
666
667 if (!tmpc)
668 return NULL;
669
670 /* basedir / tmpc / cgroup \0 */
671 len = strlen(basedir) + strlen(tmpc) + strlen(cgroup) + 3;
672 dirname = alloca(len);
673 snprintf(dirname, len, "%s/%s/%s", basedir, tmpc, cgroup);
674
675 dir = opendir(dirname);
676 if (!dir)
677 return false;
678
679 while (!readdir_r(dir, &dirent, &direntp)) {
680 struct stat mystat;
681 int rc;
682
683 if (!direntp)
684 break;
685
686 if (!strcmp(direntp->d_name, ".") ||
687 !strcmp(direntp->d_name, ".."))
688 continue;
689
690 rc = snprintf(pathname, MAXPATHLEN, "%s/%s", dirname, direntp->d_name);
691 if (rc < 0 || rc >= MAXPATHLEN) {
692 fprintf(stderr, "%s: pathname too long under %s\n", __func__, dirname);
693 continue;
694 }
695
696 ret = lstat(pathname, &mystat);
697 if (ret) {
698 fprintf(stderr, "%s: failed to stat %s: %s\n", __func__, pathname, strerror(errno));
699 continue;
700 }
701 if (!S_ISDIR(mystat.st_mode))
702 continue;
703
704 if (sz+2 >= asz) {
705 char **tmp;
706 asz += BATCH_SIZE;
707 do {
708 tmp = realloc(*list, asz * sizeof(char *));
709 } while (!tmp);
710 *list = tmp;
711 }
712 do {
713 (*list)[sz] = strdup(direntp->d_name);
714 } while (!(*list)[sz]);
715 (*list)[sz+1] = NULL;
716 sz++;
717 }
718 if (closedir(dir) < 0) {
719 fprintf(stderr, "%s: failed closedir for %s: %s\n", __func__, dirname, strerror(errno));
720 return false;
721 }
722 return true;
723}
724
725void free_key(struct cgfs_files *k)
726{
727 if (!k)
728 return;
729 free(k->name);
730 free(k);
731}
732
733void free_keys(struct cgfs_files **keys)
734{
735 int i;
736
737 if (!keys)
738 return;
739 for (i = 0; keys[i]; i++) {
740 free_key(keys[i]);
741 }
742 free(keys);
743}
744
745bool cgfs_get_value(const char *controller, const char *cgroup, const char *file, char **value)
746{
747 size_t len;
748 char *fnam, *tmpc = find_mounted_controller(controller);
749
750 if (!tmpc)
751 return false;
752 /* basedir / tmpc / cgroup / file \0 */
753 len = strlen(basedir) + strlen(tmpc) + strlen(cgroup) + strlen(file) + 4;
754 fnam = alloca(len);
755 snprintf(fnam, len, "%s/%s/%s/%s", basedir, tmpc, cgroup, file);
756
757 *value = slurp_file(fnam);
758 return *value != NULL;
759}
760
761struct cgfs_files *cgfs_get_key(const char *controller, const char *cgroup, const char *file)
762{
763 size_t len;
764 char *fnam, *tmpc = find_mounted_controller(controller);
765 struct stat sb;
766 struct cgfs_files *newkey;
767 int ret;
768
769 if (!tmpc)
770 return false;
771
772 if (file && *file == '/')
773 file++;
774
775 if (file && index(file, '/'))
776 return NULL;
777
778 /* basedir / tmpc / cgroup / file \0 */
779 len = strlen(basedir) + strlen(tmpc) + strlen(cgroup) + 3;
780 if (file)
781 len += strlen(file) + 1;
782 fnam = alloca(len);
783 snprintf(fnam, len, "%s/%s/%s%s%s", basedir, tmpc, cgroup,
784 file ? "/" : "", file ? file : "");
785
786 ret = stat(fnam, &sb);
787 if (ret < 0)
788 return NULL;
789
790 do {
791 newkey = malloc(sizeof(struct cgfs_files));
792 } while (!newkey);
793 if (file)
794 newkey->name = must_copy_string(file);
795 else if (rindex(cgroup, '/'))
796 newkey->name = must_copy_string(rindex(cgroup, '/'));
797 else
798 newkey->name = must_copy_string(cgroup);
799 newkey->uid = sb.st_uid;
800 newkey->gid = sb.st_gid;
801 newkey->mode = sb.st_mode;
802
803 return newkey;
804}
805
806bool cgfs_list_keys(const char *controller, const char *cgroup, struct cgfs_files ***keys)
807{
808 size_t len;
809 char *dirname, *tmpc = find_mounted_controller(controller);
810 char pathname[MAXPATHLEN];
811 size_t sz = 0, asz = 0;
812 struct dirent dirent, *direntp;
813 DIR *dir;
814 int ret;
815
816 *keys = NULL;
817 if (!tmpc)
818 return NULL;
819
820 /* basedir / tmpc / cgroup \0 */
821 len = strlen(basedir) + strlen(tmpc) + strlen(cgroup) + 3;
822 dirname = alloca(len);
823 snprintf(dirname, len, "%s/%s/%s", basedir, tmpc, cgroup);
824
825 dir = opendir(dirname);
826 if (!dir)
827 return false;
828
829 while (!readdir_r(dir, &dirent, &direntp)) {
830 struct stat mystat;
831 int rc;
832
833 if (!direntp)
834 break;
835
836 if (!strcmp(direntp->d_name, ".") ||
837 !strcmp(direntp->d_name, ".."))
838 continue;
839
840 rc = snprintf(pathname, MAXPATHLEN, "%s/%s", dirname, direntp->d_name);
841 if (rc < 0 || rc >= MAXPATHLEN) {
842 fprintf(stderr, "%s: pathname too long under %s\n", __func__, dirname);
843 continue;
844 }
845
846 ret = lstat(pathname, &mystat);
847 if (ret) {
848 fprintf(stderr, "%s: failed to stat %s: %s\n", __func__, pathname, strerror(errno));
849 continue;
850 }
851 if (!S_ISREG(mystat.st_mode))
852 continue;
853
854 if (sz+2 >= asz) {
855 struct cgfs_files **tmp;
856 asz += BATCH_SIZE;
857 do {
858 tmp = realloc(*keys, asz * sizeof(struct cgfs_files *));
859 } while (!tmp);
860 *keys = tmp;
861 }
862 (*keys)[sz] = cgfs_get_key(controller, cgroup, direntp->d_name);
863 (*keys)[sz+1] = NULL;
864 if (!(*keys)[sz]) {
865 fprintf(stderr, "%s: Error getting files under %s:%s\n",
866 __func__, controller, cgroup);
867 continue;
868 }
869 sz++;
870 }
871 if (closedir(dir) < 0) {
872 fprintf(stderr, "%s: failed closedir for %s: %s\n", __func__, dirname, strerror(errno));
873 return false;
874 }
875 return true;
876}
877
878bool is_child_cgroup(const char *controller, const char *cgroup, const char *f)
879{ size_t len;
880 char *fnam, *tmpc = find_mounted_controller(controller);
881 int ret;
882 struct stat sb;
883
884 if (!tmpc)
885 return false;
886 /* basedir / tmpc / cgroup / f \0 */
887 len = strlen(basedir) + strlen(tmpc) + strlen(cgroup) + strlen(f) + 4;
888 fnam = alloca(len);
889 snprintf(fnam, len, "%s/%s/%s/%s", basedir, tmpc, cgroup, f);
890
891 ret = stat(fnam, &sb);
892 if (ret < 0 || !S_ISDIR(sb.st_mode))
893 return false;
894 return true;
895}
896
897#define SEND_CREDS_OK 0
898#define SEND_CREDS_NOTSK 1
899#define SEND_CREDS_FAIL 2
900static bool recv_creds(int sock, struct ucred *cred, char *v);
901static int wait_for_pid(pid_t pid);
902static int send_creds(int sock, struct ucred *cred, char v, bool pingfirst);
903
904/*
905 * fork a task which switches to @task's namespace and writes '1'.
906 * over a unix sock so we can read the task's reaper's pid in our
907 * namespace
908 */
909static void write_task_init_pid_exit(int sock, pid_t target)
910{
911 struct ucred cred;
912 char fnam[100];
913 pid_t pid;
914 char v;
915 int fd, ret;
916
917 ret = snprintf(fnam, sizeof(fnam), "/proc/%d/ns/pid", (int)target);
918 if (ret < 0 || ret >= sizeof(fnam))
919 _exit(1);
920
921 fd = open(fnam, O_RDONLY);
922 if (fd < 0) {
923 perror("write_task_init_pid_exit open of ns/pid");
924 _exit(1);
925 }
926 if (setns(fd, 0)) {
927 perror("write_task_init_pid_exit setns 1");
928 close(fd);
929 _exit(1);
930 }
931 pid = fork();
932 if (pid < 0)
933 _exit(1);
934 if (pid != 0) {
935 if (!wait_for_pid(pid))
936 _exit(1);
937 _exit(0);
938 }
939
940 /* we are the child */
941 cred.uid = 0;
942 cred.gid = 0;
943 cred.pid = 1;
944 v = '1';
945 if (send_creds(sock, &cred, v, true) != SEND_CREDS_OK)
946 _exit(1);
947 _exit(0);
948}
949
950static pid_t get_init_pid_for_task(pid_t task)
951{
952 int sock[2];
953 pid_t pid;
954 pid_t ret = -1;
955 char v = '0';
956 struct ucred cred;
957
958 if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sock) < 0) {
959 perror("socketpair");
960 return -1;
961 }
962
963 pid = fork();
964 if (pid < 0)
965 goto out;
966 if (!pid) {
967 close(sock[1]);
968 write_task_init_pid_exit(sock[0], task);
969 _exit(0);
970 }
971
972 if (!recv_creds(sock[1], &cred, &v))
973 goto out;
974 ret = cred.pid;
975
976out:
977 close(sock[0]);
978 close(sock[1]);
979 if (pid > 0)
980 wait_for_pid(pid);
981 return ret;
982}
983
984static pid_t lookup_initpid_in_store(pid_t qpid)
985{
986 pid_t answer = 0;
987 struct stat sb;
988 struct pidns_init_store *e;
989 char fnam[100];
990
991 snprintf(fnam, 100, "/proc/%d/ns/pid", qpid);
992 store_lock();
993 if (stat(fnam, &sb) < 0)
994 goto out;
995 e = lookup_verify_initpid(&sb);
996 if (e) {
997 answer = e->initpid;
998 goto out;
999 }
1000 answer = get_init_pid_for_task(qpid);
1001 if (answer > 0)
1002 save_initpid(&sb, answer);
1003
1004out:
1005 /* we prune at end in case we are returning
1006 * the value we were about to return */
1007 prune_initpid_store();
1008 store_unlock();
1009 return answer;
1010}
1011
1012static int wait_for_pid(pid_t pid)
1013{
1014 int status, ret;
1015
1016 if (pid <= 0)
1017 return -1;
1018
1019again:
1020 ret = waitpid(pid, &status, 0);
1021 if (ret == -1) {
1022 if (errno == EINTR)
1023 goto again;
1024 return -1;
1025 }
1026 if (ret != pid)
1027 goto again;
1028 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
1029 return -1;
1030 return 0;
1031}
1032
1033
1034/*
1035 * append pid to *src.
1036 * src: a pointer to a char* in which ot append the pid.
1037 * sz: the number of characters printed so far, minus trailing \0.
1038 * asz: the allocated size so far
1039 * pid: the pid to append
1040 */
1041static void must_strcat_pid(char **src, size_t *sz, size_t *asz, pid_t pid)
1042{
1043 char tmp[30];
1044
1045 int tmplen = sprintf(tmp, "%d\n", (int)pid);
1046
1047 if (!*src || tmplen + *sz + 1 >= *asz) {
1048 char *tmp;
1049 do {
1050 tmp = realloc(*src, *asz + BUF_RESERVE_SIZE);
1051 } while (!tmp);
1052 *src = tmp;
1053 *asz += BUF_RESERVE_SIZE;
1054 }
1055 memcpy((*src) +*sz , tmp, tmplen);
1056 *sz += tmplen;
1057 (*src)[*sz] = '\0';
1058}
1059
1060/*
1061 * Given a open file * to /proc/pid/{u,g}id_map, and an id
1062 * valid in the caller's namespace, return the id mapped into
1063 * pid's namespace.
1064 * Returns the mapped id, or -1 on error.
1065 */
1066unsigned int
1067convert_id_to_ns(FILE *idfile, unsigned int in_id)
1068{
1069 unsigned int nsuid, // base id for a range in the idfile's namespace
1070 hostuid, // base id for a range in the caller's namespace
1071 count; // number of ids in this range
1072 char line[400];
1073 int ret;
1074
1075 fseek(idfile, 0L, SEEK_SET);
1076 while (fgets(line, 400, idfile)) {
1077 ret = sscanf(line, "%u %u %u\n", &nsuid, &hostuid, &count);
1078 if (ret != 3)
1079 continue;
1080 if (hostuid + count < hostuid || nsuid + count < nsuid) {
1081 /*
1082 * uids wrapped around - unexpected as this is a procfile,
1083 * so just bail.
1084 */
1085 fprintf(stderr, "pid wrapparound at entry %u %u %u in %s\n",
1086 nsuid, hostuid, count, line);
1087 return -1;
1088 }
1089 if (hostuid <= in_id && hostuid+count > in_id) {
1090 /*
1091 * now since hostuid <= in_id < hostuid+count, and
1092 * hostuid+count and nsuid+count do not wrap around,
1093 * we know that nsuid+(in_id-hostuid) which must be
1094 * less that nsuid+(count) must not wrap around
1095 */
1096 return (in_id - hostuid) + nsuid;
1097 }
1098 }
1099
1100 // no answer found
1101 return -1;
1102}
1103
1104/*
1105 * for is_privileged_over,
1106 * specify whether we require the calling uid to be root in his
1107 * namespace
1108 */
1109#define NS_ROOT_REQD true
1110#define NS_ROOT_OPT false
1111
1112#define PROCLEN 100
1113
1114static bool is_privileged_over(pid_t pid, uid_t uid, uid_t victim, bool req_ns_root)
1115{
1116 char fpath[PROCLEN];
1117 int ret;
1118 bool answer = false;
1119 uid_t nsuid;
1120
1121 if (victim == -1 || uid == -1)
1122 return false;
1123
1124 /*
1125 * If the request is one not requiring root in the namespace,
1126 * then having the same uid suffices. (i.e. uid 1000 has write
1127 * access to files owned by uid 1000
1128 */
1129 if (!req_ns_root && uid == victim)
1130 return true;
1131
1132 ret = snprintf(fpath, PROCLEN, "/proc/%d/uid_map", pid);
1133 if (ret < 0 || ret >= PROCLEN)
1134 return false;
1135 FILE *f = fopen(fpath, "r");
1136 if (!f)
1137 return false;
1138
1139 /* if caller's not root in his namespace, reject */
1140 nsuid = convert_id_to_ns(f, uid);
1141 if (nsuid)
1142 goto out;
1143
1144 /*
1145 * If victim is not mapped into caller's ns, reject.
1146 * XXX I'm not sure this check is needed given that fuse
1147 * will be sending requests where the vfs has converted
1148 */
1149 nsuid = convert_id_to_ns(f, victim);
1150 if (nsuid == -1)
1151 goto out;
1152
1153 answer = true;
1154
1155out:
1156 fclose(f);
1157 return answer;
1158}
1159
1160static bool perms_include(int fmode, mode_t req_mode)
1161{
1162 mode_t r;
1163
1164 switch (req_mode & O_ACCMODE) {
1165 case O_RDONLY:
1166 r = S_IROTH;
1167 break;
1168 case O_WRONLY:
1169 r = S_IWOTH;
1170 break;
1171 case O_RDWR:
1172 r = S_IROTH | S_IWOTH;
1173 break;
1174 default:
1175 return false;
1176 }
1177 return ((fmode & r) == r);
1178}
1179
1180
1181/*
1182 * taskcg is a/b/c
1183 * querycg is /a/b/c/d/e
1184 * we return 'd'
1185 */
1186static char *get_next_cgroup_dir(const char *taskcg, const char *querycg)
1187{
1188 char *start, *end;
1189
1190 if (strlen(taskcg) <= strlen(querycg)) {
1191 fprintf(stderr, "%s: I was fed bad input\n", __func__);
1192 return NULL;
1193 }
1194
1195 if (strcmp(querycg, "/") == 0)
1196 start = strdup(taskcg + 1);
1197 else
1198 start = strdup(taskcg + strlen(querycg) + 1);
1199 if (!start)
1200 return NULL;
1201 end = strchr(start, '/');
1202 if (end)
1203 *end = '\0';
1204 return start;
1205}
1206
1207static void stripnewline(char *x)
1208{
1209 size_t l = strlen(x);
1210 if (l && x[l-1] == '\n')
1211 x[l-1] = '\0';
1212}
1213
1214static char *get_pid_cgroup(pid_t pid, const char *contrl)
1215{
1216 char fnam[PROCLEN];
1217 FILE *f;
1218 char *answer = NULL;
1219 char *line = NULL;
1220 size_t len = 0;
1221 int ret;
1222 const char *h = find_mounted_controller(contrl);
1223 if (!h)
1224 return NULL;
1225
1226 ret = snprintf(fnam, PROCLEN, "/proc/%d/cgroup", pid);
1227 if (ret < 0 || ret >= PROCLEN)
1228 return NULL;
1229 if (!(f = fopen(fnam, "r")))
1230 return NULL;
1231
1232 while (getline(&line, &len, f) != -1) {
1233 char *c1, *c2;
1234 if (!line[0])
1235 continue;
1236 c1 = strchr(line, ':');
1237 if (!c1)
1238 goto out;
1239 c1++;
1240 c2 = strchr(c1, ':');
1241 if (!c2)
1242 goto out;
1243 *c2 = '\0';
1244 if (strcmp(c1, h) != 0)
1245 continue;
1246 c2++;
1247 stripnewline(c2);
1248 do {
1249 answer = strdup(c2);
1250 } while (!answer);
1251 break;
1252 }
1253
1254out:
1255 fclose(f);
1256 free(line);
1257 return answer;
1258}
1259
1260/*
1261 * check whether a fuse context may access a cgroup dir or file
1262 *
1263 * If file is not null, it is a cgroup file to check under cg.
1264 * If file is null, then we are checking perms on cg itself.
1265 *
1266 * For files we can check the mode of the list_keys result.
1267 * For cgroups, we must make assumptions based on the files under the
1268 * cgroup, because cgmanager doesn't tell us ownership/perms of cgroups
1269 * yet.
1270 */
1271static bool fc_may_access(struct fuse_context *fc, const char *contrl, const char *cg, const char *file, mode_t mode)
1272{
1273 struct cgfs_files *k = NULL;
1274 bool ret = false;
1275
1276 k = cgfs_get_key(contrl, cg, file);
1277 if (!k)
1278 return false;
1279
1280 if (is_privileged_over(fc->pid, fc->uid, k->uid, NS_ROOT_OPT)) {
1281 if (perms_include(k->mode >> 6, mode)) {
1282 ret = true;
1283 goto out;
1284 }
1285 }
1286 if (fc->gid == k->gid) {
1287 if (perms_include(k->mode >> 3, mode)) {
1288 ret = true;
1289 goto out;
1290 }
1291 }
1292 ret = perms_include(k->mode, mode);
1293
1294out:
1295 free_key(k);
1296 return ret;
1297}
1298
1299#define INITSCOPE "/init.scope"
1300static void prune_init_slice(char *cg)
1301{
1302 char *point;
1303 size_t cg_len = strlen(cg), initscope_len = strlen(INITSCOPE);
1304
1305 if (cg_len < initscope_len)
1306 return;
1307
1308 point = cg + cg_len - initscope_len;
1309 if (strcmp(point, INITSCOPE) == 0) {
1310 if (point == cg)
1311 *(point+1) = '\0';
1312 else
1313 *point = '\0';
1314 }
1315}
1316
1317/*
1318 * If pid is in /a/b/c/d, he may only act on things under cg=/a/b/c/d.
1319 * If pid is in /a, he may act on /a/b, but not on /b.
1320 * if the answer is false and nextcg is not NULL, then *nextcg will point
1321 * to a string containing the next cgroup directory under cg, which must be
1322 * freed by the caller.
1323 */
1324static bool caller_is_in_ancestor(pid_t pid, const char *contrl, const char *cg, char **nextcg)
1325{
1326 bool answer = false;
1327 char *c2 = get_pid_cgroup(pid, contrl);
1328 char *linecmp;
1329
1330 if (!c2)
1331 return false;
1332 prune_init_slice(c2);
1333
1334 /*
1335 * callers pass in '/' for root cgroup, otherwise they pass
1336 * in a cgroup without leading '/'
1337 */
1338 linecmp = *cg == '/' ? c2 : c2+1;
1339 if (strncmp(linecmp, cg, strlen(linecmp)) != 0) {
1340 if (nextcg) {
1341 *nextcg = get_next_cgroup_dir(linecmp, cg);
1342 }
1343 goto out;
1344 }
1345 answer = true;
1346
1347out:
1348 free(c2);
1349 return answer;
1350}
1351
1352/*
1353 * If pid is in /a/b/c, he may see that /a exists, but not /b or /a/c.
1354 */
1355static bool caller_may_see_dir(pid_t pid, const char *contrl, const char *cg)
1356{
1357 bool answer = false;
1358 char *c2, *task_cg;
1359 size_t target_len, task_len;
1360
1361 if (strcmp(cg, "/") == 0)
1362 return true;
1363
1364 c2 = get_pid_cgroup(pid, contrl);
1365 if (!c2)
1366 return false;
1367 prune_init_slice(c2);
1368
1369 task_cg = c2 + 1;
1370 target_len = strlen(cg);
1371 task_len = strlen(task_cg);
1372 if (task_len == 0) {
1373 /* Task is in the root cg, it can see everything. This case is
1374 * not handled by the strmcps below, since they test for the
1375 * last /, but that is the first / that we've chopped off
1376 * above.
1377 */
1378 answer = true;
1379 goto out;
1380 }
1381 if (strcmp(cg, task_cg) == 0) {
1382 answer = true;
1383 goto out;
1384 }
1385 if (target_len < task_len) {
1386 /* looking up a parent dir */
1387 if (strncmp(task_cg, cg, target_len) == 0 && task_cg[target_len] == '/')
1388 answer = true;
1389 goto out;
1390 }
1391 if (target_len > task_len) {
1392 /* looking up a child dir */
1393 if (strncmp(task_cg, cg, task_len) == 0 && cg[task_len] == '/')
1394 answer = true;
1395 goto out;
1396 }
1397
1398out:
1399 free(c2);
1400 return answer;
1401}
1402
1403/*
1404 * given /cgroup/freezer/a/b, return "freezer".
1405 * the returned char* should NOT be freed.
1406 */
1407static char *pick_controller_from_path(struct fuse_context *fc, const char *path)
1408{
1409 const char *p1;
1410 char *contr, *slash;
1411
1412 if (strlen(path) < 9)
1413 return NULL;
1414 if (*(path+7) != '/')
1415 return NULL;
1416 p1 = path+8;
1417 contr = strdupa(p1);
1418 if (!contr)
1419 return NULL;
1420 slash = strstr(contr, "/");
1421 if (slash)
1422 *slash = '\0';
1423
1424 int i;
1425 for (i = 0; i < num_hierarchies; i++) {
1426 if (hierarchies[i] && strcmp(hierarchies[i], contr) == 0)
1427 return hierarchies[i];
1428 }
1429 return NULL;
1430}
1431
1432/*
1433 * Find the start of cgroup in /cgroup/controller/the/cgroup/path
1434 * Note that the returned value may include files (keynames) etc
1435 */
1436static const char *find_cgroup_in_path(const char *path)
1437{
1438 const char *p1;
1439
1440 if (strlen(path) < 9)
1441 return NULL;
1442 p1 = strstr(path+8, "/");
1443 if (!p1)
1444 return NULL;
1445 return p1+1;
1446}
1447
1448/*
1449 * split the last path element from the path in @cg.
1450 * @dir is newly allocated and should be freed, @last not
1451*/
1452static void get_cgdir_and_path(const char *cg, char **dir, char **last)
1453{
1454 char *p;
1455
1456 do {
1457 *dir = strdup(cg);
1458 } while (!*dir);
1459 *last = strrchr(cg, '/');
1460 if (!*last) {
1461 *last = NULL;
1462 return;
1463 }
1464 p = strrchr(*dir, '/');
1465 *p = '\0';
1466}
1467
1468/*
1469 * FUSE ops for /cgroup
1470 */
1471
1472int cg_getattr(const char *path, struct stat *sb)
1473{
1474 struct timespec now;
1475 struct fuse_context *fc = fuse_get_context();
1476 char * cgdir = NULL;
1477 char *last = NULL, *path1, *path2;
1478 struct cgfs_files *k = NULL;
1479 const char *cgroup;
1480 const char *controller = NULL;
1481 int ret = -ENOENT;
1482
1483
1484 if (!fc)
1485 return -EIO;
1486
1487 memset(sb, 0, sizeof(struct stat));
1488
1489 if (clock_gettime(CLOCK_REALTIME, &now) < 0)
1490 return -EINVAL;
1491
1492 sb->st_uid = sb->st_gid = 0;
1493 sb->st_atim = sb->st_mtim = sb->st_ctim = now;
1494 sb->st_size = 0;
1495
1496 if (strcmp(path, "/cgroup") == 0) {
1497 sb->st_mode = S_IFDIR | 00755;
1498 sb->st_nlink = 2;
1499 return 0;
1500 }
1501
1502 controller = pick_controller_from_path(fc, path);
1503 if (!controller)
1504 return -EIO;
1505 cgroup = find_cgroup_in_path(path);
1506 if (!cgroup) {
1507 /* this is just /cgroup/controller, return it as a dir */
1508 sb->st_mode = S_IFDIR | 00755;
1509 sb->st_nlink = 2;
1510 return 0;
1511 }
1512
1513 get_cgdir_and_path(cgroup, &cgdir, &last);
1514
1515 if (!last) {
1516 path1 = "/";
1517 path2 = cgdir;
1518 } else {
1519 path1 = cgdir;
1520 path2 = last;
1521 }
1522
1523 pid_t initpid = lookup_initpid_in_store(fc->pid);
1524 if (initpid <= 0)
1525 initpid = fc->pid;
1526 /* check that cgcopy is either a child cgroup of cgdir, or listed in its keys.
1527 * Then check that caller's cgroup is under path if last is a child
1528 * cgroup, or cgdir if last is a file */
1529
1530 if (is_child_cgroup(controller, path1, path2)) {
1531 if (!caller_may_see_dir(initpid, controller, cgroup)) {
1532 ret = -ENOENT;
1533 goto out;
1534 }
1535 if (!caller_is_in_ancestor(initpid, controller, cgroup, NULL)) {
1536 /* this is just /cgroup/controller, return it as a dir */
1537 sb->st_mode = S_IFDIR | 00555;
1538 sb->st_nlink = 2;
1539 ret = 0;
1540 goto out;
1541 }
1542 if (!fc_may_access(fc, controller, cgroup, NULL, O_RDONLY)) {
1543 ret = -EACCES;
1544 goto out;
1545 }
1546
1547 // get uid, gid, from '/tasks' file and make up a mode
1548 // That is a hack, until cgmanager gains a GetCgroupPerms fn.
1549 sb->st_mode = S_IFDIR | 00755;
1550 k = cgfs_get_key(controller, cgroup, NULL);
1551 if (!k) {
1552 sb->st_uid = sb->st_gid = 0;
1553 } else {
1554 sb->st_uid = k->uid;
1555 sb->st_gid = k->gid;
1556 }
1557 free_key(k);
1558 sb->st_nlink = 2;
1559 ret = 0;
1560 goto out;
1561 }
1562
1563 if ((k = cgfs_get_key(controller, path1, path2)) != NULL) {
1564 sb->st_mode = S_IFREG | k->mode;
1565 sb->st_nlink = 1;
1566 sb->st_uid = k->uid;
1567 sb->st_gid = k->gid;
1568 sb->st_size = 0;
1569 free_key(k);
1570 if (!caller_is_in_ancestor(initpid, controller, path1, NULL)) {
1571 ret = -ENOENT;
1572 goto out;
1573 }
1574 if (!fc_may_access(fc, controller, path1, path2, O_RDONLY)) {
1575 ret = -EACCES;
1576 goto out;
1577 }
1578
1579 ret = 0;
1580 }
1581
1582out:
1583 free(cgdir);
1584 return ret;
1585}
1586
1587int cg_opendir(const char *path, struct fuse_file_info *fi)
1588{
1589 struct fuse_context *fc = fuse_get_context();
1590 const char *cgroup;
1591 struct file_info *dir_info;
1592 char *controller = NULL;
1593
1594 if (!fc)
1595 return -EIO;
1596
1597 if (strcmp(path, "/cgroup") == 0) {
1598 cgroup = NULL;
1599 controller = NULL;
1600 } else {
1601 // return list of keys for the controller, and list of child cgroups
1602 controller = pick_controller_from_path(fc, path);
1603 if (!controller)
1604 return -EIO;
1605
1606 cgroup = find_cgroup_in_path(path);
1607 if (!cgroup) {
1608 /* this is just /cgroup/controller, return its contents */
1609 cgroup = "/";
1610 }
1611 }
1612
1613 pid_t initpid = lookup_initpid_in_store(fc->pid);
1614 if (initpid <= 0)
1615 initpid = fc->pid;
1616 if (cgroup) {
1617 if (!caller_may_see_dir(initpid, controller, cgroup))
1618 return -ENOENT;
1619 if (!fc_may_access(fc, controller, cgroup, NULL, O_RDONLY))
1620 return -EACCES;
1621 }
1622
1623 /* we'll free this at cg_releasedir */
1624 dir_info = malloc(sizeof(*dir_info));
1625 if (!dir_info)
1626 return -ENOMEM;
1627 dir_info->controller = must_copy_string(controller);
1628 dir_info->cgroup = must_copy_string(cgroup);
1629 dir_info->type = LXC_TYPE_CGDIR;
1630 dir_info->buf = NULL;
1631 dir_info->file = NULL;
1632 dir_info->buflen = 0;
1633
1634 fi->fh = (unsigned long)dir_info;
1635 return 0;
1636}
1637
1638int cg_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset,
1639 struct fuse_file_info *fi)
1640{
1641 struct file_info *d = (struct file_info *)fi->fh;
1642 struct cgfs_files **list = NULL;
1643 int i, ret;
1644 char *nextcg = NULL;
1645 struct fuse_context *fc = fuse_get_context();
1646 char **clist = NULL;
1647
1648 if (d->type != LXC_TYPE_CGDIR) {
1649 fprintf(stderr, "Internal error: file cache info used in readdir\n");
1650 return -EIO;
1651 }
1652 if (!d->cgroup && !d->controller) {
1653 // ls /var/lib/lxcfs/cgroup - just show list of controllers
1654 int i;
1655
1656 for (i = 0; i < num_hierarchies; i++) {
1657 if (hierarchies[i] && filler(buf, hierarchies[i], NULL, 0) != 0) {
1658 return -EIO;
1659 }
1660 }
1661 return 0;
1662 }
1663
1664 if (!cgfs_list_keys(d->controller, d->cgroup, &list)) {
1665 // not a valid cgroup
1666 ret = -EINVAL;
1667 goto out;
1668 }
1669
1670 pid_t initpid = lookup_initpid_in_store(fc->pid);
1671 if (initpid <= 0)
1672 initpid = fc->pid;
1673 if (!caller_is_in_ancestor(initpid, d->controller, d->cgroup, &nextcg)) {
1674 if (nextcg) {
1675 ret = filler(buf, nextcg, NULL, 0);
1676 free(nextcg);
1677 if (ret != 0) {
1678 ret = -EIO;
1679 goto out;
1680 }
1681 }
1682 ret = 0;
1683 goto out;
1684 }
1685
1686 for (i = 0; list[i]; i++) {
1687 if (filler(buf, list[i]->name, NULL, 0) != 0) {
1688 ret = -EIO;
1689 goto out;
1690 }
1691 }
1692
1693 // now get the list of child cgroups
1694
1695 if (!cgfs_list_children(d->controller, d->cgroup, &clist)) {
1696 ret = 0;
1697 goto out;
1698 }
1699 for (i = 0; clist[i]; i++) {
1700 if (filler(buf, clist[i], NULL, 0) != 0) {
1701 ret = -EIO;
1702 goto out;
1703 }
1704 }
1705 ret = 0;
1706
1707out:
1708 free_keys(list);
1709 if (clist) {
1710 for (i = 0; clist[i]; i++)
1711 free(clist[i]);
1712 free(clist);
1713 }
1714 return ret;
1715}
1716
1717static void do_release_file_info(struct file_info *f)
1718{
1719 if (!f)
1720 return;
1721 free(f->controller);
1722 free(f->cgroup);
1723 free(f->file);
1724 free(f->buf);
1725 free(f);
1726}
1727
1728int cg_releasedir(const char *path, struct fuse_file_info *fi)
1729{
1730 struct file_info *d = (struct file_info *)fi->fh;
1731
1732 do_release_file_info(d);
1733 return 0;
1734}
1735
1736int cg_open(const char *path, struct fuse_file_info *fi)
1737{
1738 const char *cgroup;
1739 char *last = NULL, *path1, *path2, * cgdir = NULL, *controller;
1740 struct cgfs_files *k = NULL;
1741 struct file_info *file_info;
1742 struct fuse_context *fc = fuse_get_context();
1743 int ret;
1744
1745 if (!fc)
1746 return -EIO;
1747
1748 controller = pick_controller_from_path(fc, path);
1749 if (!controller)
1750 return -EIO;
1751 cgroup = find_cgroup_in_path(path);
1752 if (!cgroup)
1753 return -EINVAL;
1754
1755 get_cgdir_and_path(cgroup, &cgdir, &last);
1756 if (!last) {
1757 path1 = "/";
1758 path2 = cgdir;
1759 } else {
1760 path1 = cgdir;
1761 path2 = last;
1762 }
1763
1764 k = cgfs_get_key(controller, path1, path2);
1765 if (!k) {
1766 ret = -EINVAL;
1767 goto out;
1768 }
1769 free_key(k);
1770
1771 pid_t initpid = lookup_initpid_in_store(fc->pid);
1772 if (initpid <= 0)
1773 initpid = fc->pid;
1774 if (!caller_may_see_dir(initpid, controller, path1)) {
1775 ret = -ENOENT;
1776 goto out;
1777 }
1778 if (!fc_may_access(fc, controller, path1, path2, fi->flags)) {
1779 // should never get here
1780 ret = -EACCES;
1781 goto out;
1782 }
1783
1784 /* we'll free this at cg_release */
1785 file_info = malloc(sizeof(*file_info));
1786 if (!file_info) {
1787 ret = -ENOMEM;
1788 goto out;
1789 }
1790 file_info->controller = must_copy_string(controller);
1791 file_info->cgroup = must_copy_string(path1);
1792 file_info->file = must_copy_string(path2);
1793 file_info->type = LXC_TYPE_CGFILE;
1794 file_info->buf = NULL;
1795 file_info->buflen = 0;
1796
1797 fi->fh = (unsigned long)file_info;
1798 ret = 0;
1799
1800out:
1801 free(cgdir);
1802 return ret;
1803}
1804
1805int cg_release(const char *path, struct fuse_file_info *fi)
1806{
1807 struct file_info *f = (struct file_info *)fi->fh;
1808
1809 do_release_file_info(f);
1810 return 0;
1811}
1812
1813#define POLLIN_SET ( EPOLLIN | EPOLLHUP | EPOLLRDHUP )
1814
1815static bool wait_for_sock(int sock, int timeout)
1816{
1817 struct epoll_event ev;
1818 int epfd, ret, now, starttime, deltatime, saved_errno;
1819
1820 if ((starttime = time(NULL)) < 0)
1821 return false;
1822
1823 if ((epfd = epoll_create(1)) < 0) {
1824 fprintf(stderr, "Failed to create epoll socket: %m\n");
1825 return false;
1826 }
1827
1828 ev.events = POLLIN_SET;
1829 ev.data.fd = sock;
1830 if (epoll_ctl(epfd, EPOLL_CTL_ADD, sock, &ev) < 0) {
1831 fprintf(stderr, "Failed adding socket to epoll: %m\n");
1832 close(epfd);
1833 return false;
1834 }
1835
1836again:
1837 if ((now = time(NULL)) < 0) {
1838 close(epfd);
1839 return false;
1840 }
1841
1842 deltatime = (starttime + timeout) - now;
1843 if (deltatime < 0) { // timeout
1844 errno = 0;
1845 close(epfd);
1846 return false;
1847 }
1848 ret = epoll_wait(epfd, &ev, 1, 1000*deltatime + 1);
1849 if (ret < 0 && errno == EINTR)
1850 goto again;
1851 saved_errno = errno;
1852 close(epfd);
1853
1854 if (ret <= 0) {
1855 errno = saved_errno;
1856 return false;
1857 }
1858 return true;
1859}
1860
1861static int msgrecv(int sockfd, void *buf, size_t len)
1862{
1863 if (!wait_for_sock(sockfd, 2))
1864 return -1;
1865 return recv(sockfd, buf, len, MSG_DONTWAIT);
1866}
1867
1868static int send_creds(int sock, struct ucred *cred, char v, bool pingfirst)
1869{
1870 struct msghdr msg = { 0 };
1871 struct iovec iov;
1872 struct cmsghdr *cmsg;
1873 char cmsgbuf[CMSG_SPACE(sizeof(*cred))];
1874 char buf[1];
1875 buf[0] = 'p';
1876
1877 if (pingfirst) {
1878 if (msgrecv(sock, buf, 1) != 1) {
1879 fprintf(stderr, "%s: Error getting reply from server over socketpair\n",
1880 __func__);
1881 return SEND_CREDS_FAIL;
1882 }
1883 }
1884
1885 msg.msg_control = cmsgbuf;
1886 msg.msg_controllen = sizeof(cmsgbuf);
1887
1888 cmsg = CMSG_FIRSTHDR(&msg);
1889 cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
1890 cmsg->cmsg_level = SOL_SOCKET;
1891 cmsg->cmsg_type = SCM_CREDENTIALS;
1892 memcpy(CMSG_DATA(cmsg), cred, sizeof(*cred));
1893
1894 msg.msg_name = NULL;
1895 msg.msg_namelen = 0;
1896
1897 buf[0] = v;
1898 iov.iov_base = buf;
1899 iov.iov_len = sizeof(buf);
1900 msg.msg_iov = &iov;
1901 msg.msg_iovlen = 1;
1902
1903 if (sendmsg(sock, &msg, 0) < 0) {
1904 fprintf(stderr, "%s: failed at sendmsg: %s\n", __func__,
1905 strerror(errno));
1906 if (errno == 3)
1907 return SEND_CREDS_NOTSK;
1908 return SEND_CREDS_FAIL;
1909 }
1910
1911 return SEND_CREDS_OK;
1912}
1913
1914static bool recv_creds(int sock, struct ucred *cred, char *v)
1915{
1916 struct msghdr msg = { 0 };
1917 struct iovec iov;
1918 struct cmsghdr *cmsg;
1919 char cmsgbuf[CMSG_SPACE(sizeof(*cred))];
1920 char buf[1];
1921 int ret;
1922 int optval = 1;
1923
1924 *v = '1';
1925
1926 cred->pid = -1;
1927 cred->uid = -1;
1928 cred->gid = -1;
1929
1930 if (setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval)) == -1) {
1931 fprintf(stderr, "Failed to set passcred: %s\n", strerror(errno));
1932 return false;
1933 }
1934 buf[0] = '1';
1935 if (write(sock, buf, 1) != 1) {
1936 fprintf(stderr, "Failed to start write on scm fd: %s\n", strerror(errno));
1937 return false;
1938 }
1939
1940 msg.msg_name = NULL;
1941 msg.msg_namelen = 0;
1942 msg.msg_control = cmsgbuf;
1943 msg.msg_controllen = sizeof(cmsgbuf);
1944
1945 iov.iov_base = buf;
1946 iov.iov_len = sizeof(buf);
1947 msg.msg_iov = &iov;
1948 msg.msg_iovlen = 1;
1949
1950 if (!wait_for_sock(sock, 2)) {
1951 fprintf(stderr, "Timed out waiting for scm_cred: %s\n",
1952 strerror(errno));
1953 return false;
1954 }
1955 ret = recvmsg(sock, &msg, MSG_DONTWAIT);
1956 if (ret < 0) {
1957 fprintf(stderr, "Failed to receive scm_cred: %s\n",
1958 strerror(errno));
1959 return false;
1960 }
1961
1962 cmsg = CMSG_FIRSTHDR(&msg);
1963
1964 if (cmsg && cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred)) &&
1965 cmsg->cmsg_level == SOL_SOCKET &&
1966 cmsg->cmsg_type == SCM_CREDENTIALS) {
1967 memcpy(cred, CMSG_DATA(cmsg), sizeof(*cred));
1968 }
1969 *v = buf[0];
1970
1971 return true;
1972}
1973
1974
1975/*
1976 * pid_to_ns - reads pids from a ucred over a socket, then writes the
1977 * int value back over the socket. This shifts the pid from the
1978 * sender's pidns into tpid's pidns.
1979 */
1980static void pid_to_ns(int sock, pid_t tpid)
1981{
1982 char v = '0';
1983 struct ucred cred;
1984
1985 while (recv_creds(sock, &cred, &v)) {
1986 if (v == '1')
1987 _exit(0);
1988 if (write(sock, &cred.pid, sizeof(pid_t)) != sizeof(pid_t))
1989 _exit(1);
1990 }
1991 _exit(0);
1992}
1993
1994/*
1995 * pid_to_ns_wrapper: when you setns into a pidns, you yourself remain
1996 * in your old pidns. Only children which you fork will be in the target
1997 * pidns. So the pid_to_ns_wrapper does the setns, then forks a child to
1998 * actually convert pids
1999 */
2000static void pid_to_ns_wrapper(int sock, pid_t tpid)
2001{
2002 int newnsfd = -1, ret, cpipe[2];
2003 char fnam[100];
2004 pid_t cpid;
2005 char v;
2006
2007 ret = snprintf(fnam, sizeof(fnam), "/proc/%d/ns/pid", tpid);
2008 if (ret < 0 || ret >= sizeof(fnam))
2009 _exit(1);
2010 newnsfd = open(fnam, O_RDONLY);
2011 if (newnsfd < 0)
2012 _exit(1);
2013 if (setns(newnsfd, 0) < 0)
2014 _exit(1);
2015 close(newnsfd);
2016
2017 if (pipe(cpipe) < 0)
2018 _exit(1);
2019
2020 cpid = fork();
2021 if (cpid < 0)
2022 _exit(1);
2023
2024 if (!cpid) {
2025 char b = '1';
2026 close(cpipe[0]);
2027 if (write(cpipe[1], &b, sizeof(char)) < 0) {
2028 fprintf(stderr, "%s (child): erorr on write: %s\n",
2029 __func__, strerror(errno));
2030 }
2031 close(cpipe[1]);
2032 pid_to_ns(sock, tpid);
2033 _exit(1); // not reached
2034 }
2035 // give the child 1 second to be done forking and
2036 // write its ack
2037 if (!wait_for_sock(cpipe[0], 1))
2038 _exit(1);
2039 ret = read(cpipe[0], &v, 1);
2040 if (ret != sizeof(char) || v != '1')
2041 _exit(1);
2042
2043 if (!wait_for_pid(cpid))
2044 _exit(1);
2045 _exit(0);
2046}
2047
2048/*
2049 * To read cgroup files with a particular pid, we will setns into the child
2050 * pidns, open a pipe, fork a child - which will be the first to really be in
2051 * the child ns - which does the cgfs_get_value and writes the data to the pipe.
2052 */
2053bool do_read_pids(pid_t tpid, const char *contrl, const char *cg, const char *file, char **d)
2054{
2055 int sock[2] = {-1, -1};
2056 char *tmpdata = NULL;
2057 int ret;
2058 pid_t qpid, cpid = -1;
2059 bool answer = false;
2060 char v = '0';
2061 struct ucred cred;
2062 size_t sz = 0, asz = 0;
2063
2064 if (!cgfs_get_value(contrl, cg, file, &tmpdata))
2065 return false;
2066
2067 /*
2068 * Now we read the pids from returned data one by one, pass
2069 * them into a child in the target namespace, read back the
2070 * translated pids, and put them into our to-return data
2071 */
2072
2073 if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sock) < 0) {
2074 perror("socketpair");
2075 free(tmpdata);
2076 return false;
2077 }
2078
2079 cpid = fork();
2080 if (cpid == -1)
2081 goto out;
2082
2083 if (!cpid) // child - exits when done
2084 pid_to_ns_wrapper(sock[1], tpid);
2085
2086 char *ptr = tmpdata;
2087 cred.uid = 0;
2088 cred.gid = 0;
2089 while (sscanf(ptr, "%d\n", &qpid) == 1) {
2090 cred.pid = qpid;
2091 ret = send_creds(sock[0], &cred, v, true);
2092
2093 if (ret == SEND_CREDS_NOTSK)
2094 goto next;
2095 if (ret == SEND_CREDS_FAIL)
2096 goto out;
2097
2098 // read converted results
2099 if (!wait_for_sock(sock[0], 2)) {
2100 fprintf(stderr, "%s: timed out waiting for pid from child: %s\n",
2101 __func__, strerror(errno));
2102 goto out;
2103 }
2104 if (read(sock[0], &qpid, sizeof(qpid)) != sizeof(qpid)) {
2105 fprintf(stderr, "%s: error reading pid from child: %s\n",
2106 __func__, strerror(errno));
2107 goto out;
2108 }
2109 must_strcat_pid(d, &sz, &asz, qpid);
2110next:
2111 ptr = strchr(ptr, '\n');
2112 if (!ptr)
2113 break;
2114 ptr++;
2115 }
2116
2117 cred.pid = getpid();
2118 v = '1';
2119 if (send_creds(sock[0], &cred, v, true) != SEND_CREDS_OK) {
2120 // failed to ask child to exit
2121 fprintf(stderr, "%s: failed to ask child to exit: %s\n",
2122 __func__, strerror(errno));
2123 goto out;
2124 }
2125
2126 answer = true;
2127
2128out:
2129 free(tmpdata);
2130 if (cpid != -1)
2131 wait_for_pid(cpid);
2132 if (sock[0] != -1) {
2133 close(sock[0]);
2134 close(sock[1]);
2135 }
2136 return answer;
2137}
2138
2139int cg_read(const char *path, char *buf, size_t size, off_t offset,
2140 struct fuse_file_info *fi)
2141{
2142 struct fuse_context *fc = fuse_get_context();
2143 struct file_info *f = (struct file_info *)fi->fh;
2144 struct cgfs_files *k = NULL;
2145 char *data = NULL;
2146 int ret, s;
2147 bool r;
2148
2149 if (f->type != LXC_TYPE_CGFILE) {
2150 fprintf(stderr, "Internal error: directory cache info used in cg_read\n");
2151 return -EIO;
2152 }
2153
2154 if (offset)
2155 return 0;
2156
2157 if (!fc)
2158 return -EIO;
2159
2160 if (!f->controller)
2161 return -EINVAL;
2162
2163 if ((k = cgfs_get_key(f->controller, f->cgroup, f->file)) == NULL) {
2164 return -EINVAL;
2165 }
2166 free_key(k);
2167
2168
2169 if (!fc_may_access(fc, f->controller, f->cgroup, f->file, O_RDONLY)) { // should never get here
2170 ret = -EACCES;
2171 goto out;
2172 }
2173
2174 if (strcmp(f->file, "tasks") == 0 ||
2175 strcmp(f->file, "/tasks") == 0 ||
2176 strcmp(f->file, "/cgroup.procs") == 0 ||
2177 strcmp(f->file, "cgroup.procs") == 0)
2178 // special case - we have to translate the pids
2179 r = do_read_pids(fc->pid, f->controller, f->cgroup, f->file, &data);
2180 else
2181 r = cgfs_get_value(f->controller, f->cgroup, f->file, &data);
2182
2183 if (!r) {
2184 ret = -EINVAL;
2185 goto out;
2186 }
2187
2188 if (!data) {
2189 ret = 0;
2190 goto out;
2191 }
2192 s = strlen(data);
2193 if (s > size)
2194 s = size;
2195 memcpy(buf, data, s);
2196 if (s > 0 && s < size && data[s-1] != '\n')
2197 buf[s++] = '\n';
2198
2199 ret = s;
2200
2201out:
2202 free(data);
2203 return ret;
2204}
2205
2206static void pid_from_ns(int sock, pid_t tpid)
2207{
2208 pid_t vpid;
2209 struct ucred cred;
2210 char v;
2211 int ret;
2212
2213 cred.uid = 0;
2214 cred.gid = 0;
2215 while (1) {
2216 if (!wait_for_sock(sock, 2)) {
2217 fprintf(stderr, "%s: timeout reading from parent\n", __func__);
2218 _exit(1);
2219 }
2220 if ((ret = read(sock, &vpid, sizeof(pid_t))) != sizeof(pid_t)) {
2221 fprintf(stderr, "%s: bad read from parent: %s\n",
2222 __func__, strerror(errno));
2223 _exit(1);
2224 }
2225 if (vpid == -1) // done
2226 break;
2227 v = '0';
2228 cred.pid = vpid;
2229 if (send_creds(sock, &cred, v, true) != SEND_CREDS_OK) {
2230 v = '1';
2231 cred.pid = getpid();
2232 if (send_creds(sock, &cred, v, false) != SEND_CREDS_OK)
2233 _exit(1);
2234 }
2235 }
2236 _exit(0);
2237}
2238
2239static void pid_from_ns_wrapper(int sock, pid_t tpid)
2240{
2241 int newnsfd = -1, ret, cpipe[2];
2242 char fnam[100];
2243 pid_t cpid;
2244 char v;
2245
2246 ret = snprintf(fnam, sizeof(fnam), "/proc/%d/ns/pid", tpid);
2247 if (ret < 0 || ret >= sizeof(fnam))
2248 _exit(1);
2249 newnsfd = open(fnam, O_RDONLY);
2250 if (newnsfd < 0)
2251 _exit(1);
2252 if (setns(newnsfd, 0) < 0)
2253 _exit(1);
2254 close(newnsfd);
2255
2256 if (pipe(cpipe) < 0)
2257 _exit(1);
2258
2259loop:
2260 cpid = fork();
2261
2262 if (cpid < 0)
2263 _exit(1);
2264
2265 if (!cpid) {
2266 char b = '1';
2267 close(cpipe[0]);
2268 if (write(cpipe[1], &b, sizeof(char)) < 0) {
2269 fprintf(stderr, "%s (child): erorr on write: %s\n",
2270 __func__, strerror(errno));
2271 }
2272 close(cpipe[1]);
2273 pid_from_ns(sock, tpid);
2274 }
2275
2276 // give the child 1 second to be done forking and
2277 // write its ack
2278 if (!wait_for_sock(cpipe[0], 1))
2279 goto again;
2280 ret = read(cpipe[0], &v, 1);
2281 if (ret != sizeof(char) || v != '1') {
2282 goto again;
2283 }
2284
2285 if (!wait_for_pid(cpid))
2286 _exit(1);
2287 _exit(0);
2288
2289again:
2290 kill(cpid, SIGKILL);
2291 wait_for_pid(cpid);
2292 goto loop;
2293}
2294
2295/*
2296 * Given host @uid, return the uid to which it maps in
2297 * @pid's user namespace, or -1 if none.
2298 */
2299bool hostuid_to_ns(uid_t uid, pid_t pid, uid_t *answer)
2300{
2301 FILE *f;
2302 char line[400];
2303
2304 sprintf(line, "/proc/%d/uid_map", pid);
2305 if ((f = fopen(line, "r")) == NULL) {
2306 return false;
2307 }
2308
2309 *answer = convert_id_to_ns(f, uid);
2310 fclose(f);
2311
2312 if (*answer == -1)
2313 return false;
2314 return true;
2315}
2316
2317/*
2318 * get_pid_creds: get the real uid and gid of @pid from
2319 * /proc/$$/status
2320 * (XXX should we use euid here?)
2321 */
2322void get_pid_creds(pid_t pid, uid_t *uid, gid_t *gid)
2323{
2324 char line[400];
2325 uid_t u;
2326 gid_t g;
2327 FILE *f;
2328
2329 *uid = -1;
2330 *gid = -1;
2331 sprintf(line, "/proc/%d/status", pid);
2332 if ((f = fopen(line, "r")) == NULL) {
2333 fprintf(stderr, "Error opening %s: %s\n", line, strerror(errno));
2334 return;
2335 }
2336 while (fgets(line, 400, f)) {
2337 if (strncmp(line, "Uid:", 4) == 0) {
2338 if (sscanf(line+4, "%u", &u) != 1) {
2339 fprintf(stderr, "bad uid line for pid %u\n", pid);
2340 fclose(f);
2341 return;
2342 }
2343 *uid = u;
2344 } else if (strncmp(line, "Gid:", 4) == 0) {
2345 if (sscanf(line+4, "%u", &g) != 1) {
2346 fprintf(stderr, "bad gid line for pid %u\n", pid);
2347 fclose(f);
2348 return;
2349 }
2350 *gid = g;
2351 }
2352 }
2353 fclose(f);
2354}
2355
2356/*
2357 * May the requestor @r move victim @v to a new cgroup?
2358 * This is allowed if
2359 * . they are the same task
2360 * . they are ownedy by the same uid
2361 * . @r is root on the host, or
2362 * . @v's uid is mapped into @r's where @r is root.
2363 */
2364bool may_move_pid(pid_t r, uid_t r_uid, pid_t v)
2365{
2366 uid_t v_uid, tmpuid;
2367 gid_t v_gid;
2368
2369 if (r == v)
2370 return true;
2371 if (r_uid == 0)
2372 return true;
2373 get_pid_creds(v, &v_uid, &v_gid);
2374 if (r_uid == v_uid)
2375 return true;
2376 if (hostuid_to_ns(r_uid, r, &tmpuid) && tmpuid == 0
2377 && hostuid_to_ns(v_uid, r, &tmpuid))
2378 return true;
2379 return false;
2380}
2381
2382static bool do_write_pids(pid_t tpid, uid_t tuid, const char *contrl, const char *cg,
2383 const char *file, const char *buf)
2384{
2385 int sock[2] = {-1, -1};
2386 pid_t qpid, cpid = -1;
2387 FILE *pids_file = NULL;
2388 bool answer = false, fail = false;
2389
2390 pids_file = open_pids_file(contrl, cg);
2391 if (!pids_file)
2392 return false;
2393
2394 /*
2395 * write the pids to a socket, have helper in writer's pidns
2396 * call movepid for us
2397 */
2398 if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sock) < 0) {
2399 perror("socketpair");
2400 goto out;
2401 }
2402
2403 cpid = fork();
2404 if (cpid == -1)
2405 goto out;
2406
2407 if (!cpid) { // child
2408 fclose(pids_file);
2409 pid_from_ns_wrapper(sock[1], tpid);
2410 }
2411
2412 const char *ptr = buf;
2413 while (sscanf(ptr, "%d", &qpid) == 1) {
2414 struct ucred cred;
2415 char v;
2416
2417 if (write(sock[0], &qpid, sizeof(qpid)) != sizeof(qpid)) {
2418 fprintf(stderr, "%s: error writing pid to child: %s\n",
2419 __func__, strerror(errno));
2420 goto out;
2421 }
2422
2423 if (recv_creds(sock[0], &cred, &v)) {
2424 if (v == '0') {
2425 if (!may_move_pid(tpid, tuid, cred.pid)) {
2426 fail = true;
2427 break;
2428 }
2429 if (fprintf(pids_file, "%d", (int) cred.pid) < 0)
2430 fail = true;
2431 }
2432 }
2433
2434 ptr = strchr(ptr, '\n');
2435 if (!ptr)
2436 break;
2437 ptr++;
2438 }
2439
2440 /* All good, write the value */
2441 qpid = -1;
2442 if (write(sock[0], &qpid ,sizeof(qpid)) != sizeof(qpid))
2443 fprintf(stderr, "Warning: failed to ask child to exit\n");
2444
2445 if (!fail)
2446 answer = true;
2447
2448out:
2449 if (cpid != -1)
2450 wait_for_pid(cpid);
2451 if (sock[0] != -1) {
2452 close(sock[0]);
2453 close(sock[1]);
2454 }
2455 if (pids_file) {
2456 if (fclose(pids_file) != 0)
2457 answer = false;
2458 }
2459 return answer;
2460}
2461
2462int cg_write(const char *path, const char *buf, size_t size, off_t offset,
2463 struct fuse_file_info *fi)
2464{
2465 struct fuse_context *fc = fuse_get_context();
2466 char *localbuf = NULL;
2467 struct cgfs_files *k = NULL;
2468 struct file_info *f = (struct file_info *)fi->fh;
2469 bool r;
2470
2471 if (f->type != LXC_TYPE_CGFILE) {
2472 fprintf(stderr, "Internal error: directory cache info used in cg_write\n");
2473 return -EIO;
2474 }
2475
2476 if (offset)
2477 return 0;
2478
2479 if (!fc)
2480 return -EIO;
2481
2482 localbuf = alloca(size+1);
2483 localbuf[size] = '\0';
2484 memcpy(localbuf, buf, size);
2485
2486 if ((k = cgfs_get_key(f->controller, f->cgroup, f->file)) == NULL) {
2487 size = -EINVAL;
2488 goto out;
2489 }
2490
2491 if (!fc_may_access(fc, f->controller, f->cgroup, f->file, O_WRONLY)) {
2492 size = -EACCES;
2493 goto out;
2494 }
2495
2496 if (strcmp(f->file, "tasks") == 0 ||
2497 strcmp(f->file, "/tasks") == 0 ||
2498 strcmp(f->file, "/cgroup.procs") == 0 ||
2499 strcmp(f->file, "cgroup.procs") == 0)
2500 // special case - we have to translate the pids
2501 r = do_write_pids(fc->pid, fc->uid, f->controller, f->cgroup, f->file, localbuf);
2502 else
2503 r = cgfs_set_value(f->controller, f->cgroup, f->file, localbuf);
2504
2505 if (!r)
2506 size = -EINVAL;
2507
2508out:
2509 free_key(k);
2510 return size;
2511}
2512
2513int cg_chown(const char *path, uid_t uid, gid_t gid)
2514{
2515 struct fuse_context *fc = fuse_get_context();
2516 char *cgdir = NULL, *last = NULL, *path1, *path2, *controller;
2517 struct cgfs_files *k = NULL;
2518 const char *cgroup;
2519 int ret;
2520
2521 if (!fc)
2522 return -EIO;
2523
2524 if (strcmp(path, "/cgroup") == 0)
2525 return -EINVAL;
2526
2527 controller = pick_controller_from_path(fc, path);
2528 if (!controller)
2529 return -EINVAL;
2530 cgroup = find_cgroup_in_path(path);
2531 if (!cgroup)
2532 /* this is just /cgroup/controller */
2533 return -EINVAL;
2534
2535 get_cgdir_and_path(cgroup, &cgdir, &last);
2536
2537 if (!last) {
2538 path1 = "/";
2539 path2 = cgdir;
2540 } else {
2541 path1 = cgdir;
2542 path2 = last;
2543 }
2544
2545 if (is_child_cgroup(controller, path1, path2)) {
2546 // get uid, gid, from '/tasks' file and make up a mode
2547 // That is a hack, until cgmanager gains a GetCgroupPerms fn.
2548 k = cgfs_get_key(controller, cgroup, "tasks");
2549
2550 } else
2551 k = cgfs_get_key(controller, path1, path2);
2552
2553 if (!k) {
2554 ret = -EINVAL;
2555 goto out;
2556 }
2557
2558 /*
2559 * This being a fuse request, the uid and gid must be valid
2560 * in the caller's namespace. So we can just check to make
2561 * sure that the caller is root in his uid, and privileged
2562 * over the file's current owner.
2563 */
2564 if (!is_privileged_over(fc->pid, fc->uid, k->uid, NS_ROOT_REQD)) {
2565 ret = -EACCES;
2566 goto out;
2567 }
2568
2569 ret = cgfs_chown_file(controller, cgroup, uid, gid);
2570
2571out:
2572 free_key(k);
2573 free(cgdir);
2574
2575 return ret;
2576}
2577
2578int cg_chmod(const char *path, mode_t mode)
2579{
2580 struct fuse_context *fc = fuse_get_context();
2581 char * cgdir = NULL, *last = NULL, *path1, *path2, *controller;
2582 struct cgfs_files *k = NULL;
2583 const char *cgroup;
2584 int ret;
2585
2586 if (!fc)
2587 return -EIO;
2588
2589 if (strcmp(path, "/cgroup") == 0)
2590 return -EINVAL;
2591
2592 controller = pick_controller_from_path(fc, path);
2593 if (!controller)
2594 return -EINVAL;
2595 cgroup = find_cgroup_in_path(path);
2596 if (!cgroup)
2597 /* this is just /cgroup/controller */
2598 return -EINVAL;
2599
2600 get_cgdir_and_path(cgroup, &cgdir, &last);
2601
2602 if (!last) {
2603 path1 = "/";
2604 path2 = cgdir;
2605 } else {
2606 path1 = cgdir;
2607 path2 = last;
2608 }
2609
2610 if (is_child_cgroup(controller, path1, path2)) {
2611 // get uid, gid, from '/tasks' file and make up a mode
2612 // That is a hack, until cgmanager gains a GetCgroupPerms fn.
2613 k = cgfs_get_key(controller, cgroup, "tasks");
2614
2615 } else
2616 k = cgfs_get_key(controller, path1, path2);
2617
2618 if (!k) {
2619 ret = -EINVAL;
2620 goto out;
2621 }
2622
2623 /*
2624 * This being a fuse request, the uid and gid must be valid
2625 * in the caller's namespace. So we can just check to make
2626 * sure that the caller is root in his uid, and privileged
2627 * over the file's current owner.
2628 */
2629 if (!is_privileged_over(fc->pid, fc->uid, k->uid, NS_ROOT_OPT)) {
2630 ret = -EPERM;
2631 goto out;
2632 }
2633
2634 if (!cgfs_chmod_file(controller, cgroup, mode)) {
2635 ret = -EINVAL;
2636 goto out;
2637 }
2638
2639 ret = 0;
2640out:
2641 free_key(k);
2642 free(cgdir);
2643 return ret;
2644}
2645
2646int cg_mkdir(const char *path, mode_t mode)
2647{
2648 struct fuse_context *fc = fuse_get_context();
2649 char *last = NULL, *path1, *cgdir = NULL, *controller, *next = NULL;
2650 const char *cgroup;
2651 int ret;
2652
2653 if (!fc)
2654 return -EIO;
2655
2656
2657 controller = pick_controller_from_path(fc, path);
2658 if (!controller)
2659 return -EINVAL;
2660
2661 cgroup = find_cgroup_in_path(path);
2662 if (!cgroup)
2663 return -EINVAL;
2664
2665 get_cgdir_and_path(cgroup, &cgdir, &last);
2666 if (!last)
2667 path1 = "/";
2668 else
2669 path1 = cgdir;
2670
2671 pid_t initpid = lookup_initpid_in_store(fc->pid);
2672 if (initpid <= 0)
2673 initpid = fc->pid;
2674 if (!caller_is_in_ancestor(initpid, controller, path1, &next)) {
2675 if (!next)
2676 ret = -EINVAL;
2677 else if (last && strcmp(next, last) == 0)
2678 ret = -EEXIST;
2679 else
2680 ret = -ENOENT;
2681 goto out;
2682 }
2683
2684 if (!fc_may_access(fc, controller, path1, NULL, O_RDWR)) {
2685 ret = -EACCES;
2686 goto out;
2687 }
2688 if (!caller_is_in_ancestor(initpid, controller, path1, NULL)) {
2689 ret = -EACCES;
2690 goto out;
2691 }
2692
2693 ret = cgfs_create(controller, cgroup, fc->uid, fc->gid);
2694
2695out:
2696 free(cgdir);
2697 free(next);
2698 return ret;
2699}
2700
2701int cg_rmdir(const char *path)
2702{
2703 struct fuse_context *fc = fuse_get_context();
2704 char *last = NULL, *cgdir = NULL, *controller, *next = NULL;
2705 const char *cgroup;
2706 int ret;
2707
2708 if (!fc)
2709 return -EIO;
2710
2711 controller = pick_controller_from_path(fc, path);
2712 if (!controller)
2713 return -EINVAL;
2714
2715 cgroup = find_cgroup_in_path(path);
2716 if (!cgroup)
2717 return -EINVAL;
2718
2719 get_cgdir_and_path(cgroup, &cgdir, &last);
2720 if (!last) {
2721 ret = -EINVAL;
2722 goto out;
2723 }
2724
2725 pid_t initpid = lookup_initpid_in_store(fc->pid);
2726 if (initpid <= 0)
2727 initpid = fc->pid;
2728 if (!caller_is_in_ancestor(initpid, controller, cgroup, &next)) {
2729 if (!last || strcmp(next, last) == 0)
2730 ret = -EBUSY;
2731 else
2732 ret = -ENOENT;
2733 goto out;
2734 }
2735
2736 if (!fc_may_access(fc, controller, cgdir, NULL, O_WRONLY)) {
2737 ret = -EACCES;
2738 goto out;
2739 }
2740 if (!caller_is_in_ancestor(initpid, controller, cgroup, NULL)) {
2741 ret = -EACCES;
2742 goto out;
2743 }
2744
2745 if (!cgfs_remove(controller, cgroup)) {
2746 ret = -EINVAL;
2747 goto out;
2748 }
2749
2750 ret = 0;
2751
2752out:
2753 free(cgdir);
2754 free(next);
2755 return ret;
2756}
2757
2758static bool startswith(const char *line, const char *pref)
2759{
2760 if (strncmp(line, pref, strlen(pref)) == 0)
2761 return true;
2762 return false;
2763}
2764
2765static void get_mem_cached(char *memstat, unsigned long *v)
2766{
2767 char *eol;
2768
2769 *v = 0;
2770 while (*memstat) {
2771 if (startswith(memstat, "total_cache")) {
2772 sscanf(memstat + 11, "%lu", v);
2773 *v /= 1024;
2774 return;
2775 }
2776 eol = strchr(memstat, '\n');
2777 if (!eol)
2778 return;
2779 memstat = eol+1;
2780 }
2781}
2782
2783static void get_blkio_io_value(char *str, unsigned major, unsigned minor, char *iotype, unsigned long *v)
2784{
2785 char *eol;
2786 char key[32];
2787
2788 memset(key, 0, 32);
2789 snprintf(key, 32, "%u:%u %s", major, minor, iotype);
2790
2791 size_t len = strlen(key);
2792 *v = 0;
2793
2794 while (*str) {
2795 if (startswith(str, key)) {
2796 sscanf(str + len, "%lu", v);
2797 return;
2798 }
2799 eol = strchr(str, '\n');
2800 if (!eol)
2801 return;
2802 str = eol+1;
2803 }
2804}
2805
2806static int read_file(const char *path, char *buf, size_t size,
2807 struct file_info *d)
2808{
2809 size_t linelen = 0, total_len = 0, rv = 0;
2810 char *line = NULL;
2811 char *cache = d->buf;
2812 size_t cache_size = d->buflen;
2813 FILE *f = fopen(path, "r");
2814 if (!f)
2815 return 0;
2816
2817 while (getline(&line, &linelen, f) != -1) {
2818 size_t l = snprintf(cache, cache_size, "%s", line);
2819 if (l < 0) {
2820 perror("Error writing to cache");
2821 rv = 0;
2822 goto err;
2823 }
2824 if (l >= cache_size) {
2825 fprintf(stderr, "Internal error: truncated write to cache\n");
2826 rv = 0;
2827 goto err;
2828 }
2829 cache += l;
2830 cache_size -= l;
2831 total_len += l;
2832 }
2833
2834 d->size = total_len;
2835 if (total_len > size ) total_len = size;
2836
2837 /* read from off 0 */
2838 memcpy(buf, d->buf, total_len);
2839 rv = total_len;
2840 err:
2841 fclose(f);
2842 free(line);
2843 return rv;
2844}
2845
2846/*
2847 * FUSE ops for /proc
2848 */
2849
2850static unsigned long get_memlimit(const char *cgroup)
2851{
2852 char *memlimit_str = NULL;
2853 unsigned long memlimit = -1;
2854
2855 if (cgfs_get_value("memory", cgroup, "memory.limit_in_bytes", &memlimit_str))
2856 memlimit = strtoul(memlimit_str, NULL, 10);
2857
2858 free(memlimit_str);
2859
2860 return memlimit;
2861}
2862
2863static unsigned long get_min_memlimit(const char *cgroup)
2864{
2865 char *copy = strdupa(cgroup);
2866 unsigned long memlimit = 0, retlimit;
2867
2868 retlimit = get_memlimit(copy);
2869
2870 while (strcmp(copy, "/") != 0) {
2871 copy = dirname(copy);
2872 memlimit = get_memlimit(copy);
2873 if (memlimit != -1 && memlimit < retlimit)
2874 retlimit = memlimit;
2875 };
2876
2877 return retlimit;
2878}
2879
2880static int proc_meminfo_read(char *buf, size_t size, off_t offset,
2881 struct fuse_file_info *fi)
2882{
2883 struct fuse_context *fc = fuse_get_context();
2884 struct file_info *d = (struct file_info *)fi->fh;
2885 char *cg;
2886 char *memusage_str = NULL, *memstat_str = NULL,
2887 *memswlimit_str = NULL, *memswusage_str = NULL,
2888 *memswlimit_default_str = NULL, *memswusage_default_str = NULL;
2889 unsigned long memlimit = 0, memusage = 0, memswlimit = 0, memswusage = 0,
2890 cached = 0, hosttotal = 0;
2891 char *line = NULL;
2892 size_t linelen = 0, total_len = 0, rv = 0;
2893 char *cache = d->buf;
2894 size_t cache_size = d->buflen;
2895 FILE *f = NULL;
2896
2897 if (offset){
2898 if (offset > d->size)
2899 return -EINVAL;
2900 if (!d->cached)
2901 return 0;
2902 int left = d->size - offset;
2903 total_len = left > size ? size: left;
2904 memcpy(buf, cache + offset, total_len);
2905 return total_len;
2906 }
2907
2908 pid_t initpid = lookup_initpid_in_store(fc->pid);
2909 if (initpid <= 0)
2910 initpid = fc->pid;
2911 cg = get_pid_cgroup(initpid, "memory");
2912 if (!cg)
2913 return read_file("/proc/meminfo", buf, size, d);
2914
2915 memlimit = get_min_memlimit(cg);
2916 if (!cgfs_get_value("memory", cg, "memory.usage_in_bytes", &memusage_str))
2917 goto err;
2918 if (!cgfs_get_value("memory", cg, "memory.stat", &memstat_str))
2919 goto err;
2920
2921 // Following values are allowed to fail, because swapaccount might be turned
2922 // off for current kernel
2923 if(cgfs_get_value("memory", cg, "memory.memsw.limit_in_bytes", &memswlimit_str) &&
2924 cgfs_get_value("memory", cg, "memory.memsw.usage_in_bytes", &memswusage_str))
2925 {
2926 /* If swapaccounting is turned on, then default value is assumed to be that of cgroup / */
2927 if (!cgfs_get_value("memory", "/", "memory.memsw.limit_in_bytes", &memswlimit_default_str))
2928 goto err;
2929 if (!cgfs_get_value("memory", "/", "memory.memsw.usage_in_bytes", &memswusage_default_str))
2930 goto err;
2931
2932 memswlimit = strtoul(memswlimit_str, NULL, 10);
2933 memswusage = strtoul(memswusage_str, NULL, 10);
2934
2935 if (!strcmp(memswlimit_str, memswlimit_default_str))
2936 memswlimit = 0;
2937 if (!strcmp(memswusage_str, memswusage_default_str))
2938 memswusage = 0;
2939
2940 memswlimit = memswlimit / 1024;
2941 memswusage = memswusage / 1024;
2942 }
2943
2944 memusage = strtoul(memusage_str, NULL, 10);
2945 memlimit /= 1024;
2946 memusage /= 1024;
2947
2948 get_mem_cached(memstat_str, &cached);
2949
2950 f = fopen("/proc/meminfo", "r");
2951 if (!f)
2952 goto err;
2953
2954 while (getline(&line, &linelen, f) != -1) {
2955 size_t l;
2956 char *printme, lbuf[100];
2957
2958 memset(lbuf, 0, 100);
2959 if (startswith(line, "MemTotal:")) {
2960 sscanf(line+14, "%lu", &hosttotal);
2961 if (hosttotal < memlimit)
2962 memlimit = hosttotal;
2963 snprintf(lbuf, 100, "MemTotal: %8lu kB\n", memlimit);
2964 printme = lbuf;
2965 } else if (startswith(line, "MemFree:")) {
2966 snprintf(lbuf, 100, "MemFree: %8lu kB\n", memlimit - memusage);
2967 printme = lbuf;
2968 } else if (startswith(line, "MemAvailable:")) {
2969 snprintf(lbuf, 100, "MemAvailable: %8lu kB\n", memlimit - memusage);
2970 printme = lbuf;
2971 } else if (startswith(line, "SwapTotal:") && memswlimit > 0) {
2972 snprintf(lbuf, 100, "SwapTotal: %8lu kB\n", memswlimit - memlimit);
2973 printme = lbuf;
2974 } else if (startswith(line, "SwapFree:") && memswlimit > 0 && memswusage > 0) {
2975 snprintf(lbuf, 100, "SwapFree: %8lu kB\n",
2976 (memswlimit - memlimit) - (memswusage - memusage));
2977 printme = lbuf;
2978 } else if (startswith(line, "Buffers:")) {
2979 snprintf(lbuf, 100, "Buffers: %8lu kB\n", 0UL);
2980 printme = lbuf;
2981 } else if (startswith(line, "Cached:")) {
2982 snprintf(lbuf, 100, "Cached: %8lu kB\n", cached);
2983 printme = lbuf;
2984 } else if (startswith(line, "SwapCached:")) {
2985 snprintf(lbuf, 100, "SwapCached: %8lu kB\n", 0UL);
2986 printme = lbuf;
2987 } else
2988 printme = line;
2989
2990 l = snprintf(cache, cache_size, "%s", printme);
2991 if (l < 0) {
2992 perror("Error writing to cache");
2993 rv = 0;
2994 goto err;
2995
2996 }
2997 if (l >= cache_size) {
2998 fprintf(stderr, "Internal error: truncated write to cache\n");
2999 rv = 0;
3000 goto err;
3001 }
3002
3003 cache += l;
3004 cache_size -= l;
3005 total_len += l;
3006 }
3007
3008 d->cached = 1;
3009 d->size = total_len;
3010 if (total_len > size ) total_len = size;
3011 memcpy(buf, d->buf, total_len);
3012
3013 rv = total_len;
3014err:
3015 if (f)
3016 fclose(f);
3017 free(line);
3018 free(cg);
3019 free(memusage_str);
3020 free(memswlimit_str);
3021 free(memswusage_str);
3022 free(memstat_str);
3023 free(memswlimit_default_str);
3024 free(memswusage_default_str);
3025 return rv;
3026}
3027
3028/*
3029 * Read the cpuset.cpus for cg
3030 * Return the answer in a newly allocated string which must be freed
3031 */
3032static char *get_cpuset(const char *cg)
3033{
3034 char *answer;
3035
3036 if (!cgfs_get_value("cpuset", cg, "cpuset.cpus", &answer))
3037 return NULL;
3038 return answer;
3039}
3040
3041bool cpu_in_cpuset(int cpu, const char *cpuset);
3042
3043static bool cpuline_in_cpuset(const char *line, const char *cpuset)
3044{
3045 int cpu;
3046
3047 if (sscanf(line, "processor : %d", &cpu) != 1)
3048 return false;
3049 return cpu_in_cpuset(cpu, cpuset);
3050}
3051
3052/*
3053 * check whether this is a '^processor" line in /proc/cpuinfo
3054 */
3055static bool is_processor_line(const char *line)
3056{
3057 int cpu;
3058
3059 if (sscanf(line, "processor : %d", &cpu) == 1)
3060 return true;
3061 return false;
3062}
3063
3064static int proc_cpuinfo_read(char *buf, size_t size, off_t offset,
3065 struct fuse_file_info *fi)
3066{
3067 struct fuse_context *fc = fuse_get_context();
3068 struct file_info *d = (struct file_info *)fi->fh;
3069 char *cg;
3070 char *cpuset = NULL;
3071 char *line = NULL;
3072 size_t linelen = 0, total_len = 0, rv = 0;
3073 bool am_printing = false;
3074 int curcpu = -1;
3075 char *cache = d->buf;
3076 size_t cache_size = d->buflen;
3077 FILE *f = NULL;
3078
3079 if (offset){
3080 if (offset > d->size)
3081 return -EINVAL;
3082 if (!d->cached)
3083 return 0;
3084 int left = d->size - offset;
3085 total_len = left > size ? size: left;
3086 memcpy(buf, cache + offset, total_len);
3087 return total_len;
3088 }
3089
3090 pid_t initpid = lookup_initpid_in_store(fc->pid);
3091 if (initpid <= 0)
3092 initpid = fc->pid;
3093 cg = get_pid_cgroup(initpid, "cpuset");
3094 if (!cg)
3095 return read_file("proc/cpuinfo", buf, size, d);
3096
3097 cpuset = get_cpuset(cg);
3098 if (!cpuset)
3099 goto err;
3100
3101 f = fopen("/proc/cpuinfo", "r");
3102 if (!f)
3103 goto err;
3104
3105 while (getline(&line, &linelen, f) != -1) {
3106 size_t l;
3107 if (is_processor_line(line)) {
3108 am_printing = cpuline_in_cpuset(line, cpuset);
3109 if (am_printing) {
3110 curcpu ++;
3111 l = snprintf(cache, cache_size, "processor : %d\n", curcpu);
3112 if (l < 0) {
3113 perror("Error writing to cache");
3114 rv = 0;
3115 goto err;
3116 }
3117 if (l >= cache_size) {
3118 fprintf(stderr, "Internal error: truncated write to cache\n");
3119 rv = 0;
3120 goto err;
3121 }
3122 cache += l;
3123 cache_size -= l;
3124 total_len += l;
3125 }
3126 continue;
3127 }
3128 if (am_printing) {
3129 l = snprintf(cache, cache_size, "%s", line);
3130 if (l < 0) {
3131 perror("Error writing to cache");
3132 rv = 0;
3133 goto err;
3134 }
3135 if (l >= cache_size) {
3136 fprintf(stderr, "Internal error: truncated write to cache\n");
3137 rv = 0;
3138 goto err;
3139 }
3140 cache += l;
3141 cache_size -= l;
3142 total_len += l;
3143 }
3144 }
3145
3146 d->cached = 1;
3147 d->size = total_len;
3148 if (total_len > size ) total_len = size;
3149
3150 /* read from off 0 */
3151 memcpy(buf, d->buf, total_len);
3152 rv = total_len;
3153err:
3154 if (f)
3155 fclose(f);
3156 free(line);
3157 free(cpuset);
3158 free(cg);
3159 return rv;
3160}
3161
3162static int proc_stat_read(char *buf, size_t size, off_t offset,
3163 struct fuse_file_info *fi)
3164{
3165 struct fuse_context *fc = fuse_get_context();
3166 struct file_info *d = (struct file_info *)fi->fh;
3167 char *cg;
3168 char *cpuset = NULL;
3169 char *line = NULL;
3170 size_t linelen = 0, total_len = 0, rv = 0;
3171 int curcpu = -1; /* cpu numbering starts at 0 */
3172 unsigned long user = 0, nice = 0, system = 0, idle = 0, iowait = 0, irq = 0, softirq = 0, steal = 0, guest = 0;
3173 unsigned long user_sum = 0, nice_sum = 0, system_sum = 0, idle_sum = 0, iowait_sum = 0,
3174 irq_sum = 0, softirq_sum = 0, steal_sum = 0, guest_sum = 0;
3175#define CPUALL_MAX_SIZE BUF_RESERVE_SIZE
3176 char cpuall[CPUALL_MAX_SIZE];
3177 /* reserve for cpu all */
3178 char *cache = d->buf + CPUALL_MAX_SIZE;
3179 size_t cache_size = d->buflen - CPUALL_MAX_SIZE;
3180 FILE *f = NULL;
3181
3182 if (offset){
3183 if (offset > d->size)
3184 return -EINVAL;
3185 if (!d->cached)
3186 return 0;
3187 int left = d->size - offset;
3188 total_len = left > size ? size: left;
3189 memcpy(buf, d->buf + offset, total_len);
3190 return total_len;
3191 }
3192
3193 pid_t initpid = lookup_initpid_in_store(fc->pid);
3194 if (initpid <= 0)
3195 initpid = fc->pid;
3196 cg = get_pid_cgroup(initpid, "cpuset");
3197 if (!cg)
3198 return read_file("/proc/stat", buf, size, d);
3199
3200 cpuset = get_cpuset(cg);
3201 if (!cpuset)
3202 goto err;
3203
3204 f = fopen("/proc/stat", "r");
3205 if (!f)
3206 goto err;
3207
3208 //skip first line
3209 if (getline(&line, &linelen, f) < 0) {
3210 fprintf(stderr, "proc_stat_read read first line failed\n");
3211 goto err;
3212 }
3213
3214 while (getline(&line, &linelen, f) != -1) {
3215 size_t l;
3216 int cpu;
3217 char cpu_char[10]; /* That's a lot of cores */
3218 char *c;
3219
3220 if (sscanf(line, "cpu%9[^ ]", cpu_char) != 1) {
3221 /* not a ^cpuN line containing a number N, just print it */
3222 l = snprintf(cache, cache_size, "%s", line);
3223 if (l < 0) {
3224 perror("Error writing to cache");
3225 rv = 0;
3226 goto err;
3227 }
3228 if (l >= cache_size) {
3229 fprintf(stderr, "Internal error: truncated write to cache\n");
3230 rv = 0;
3231 goto err;
3232 }
3233 cache += l;
3234 cache_size -= l;
3235 total_len += l;
3236 continue;
3237 }
3238
3239 if (sscanf(cpu_char, "%d", &cpu) != 1)
3240 continue;
3241 if (!cpu_in_cpuset(cpu, cpuset))
3242 continue;
3243 curcpu ++;
3244
3245 c = strchr(line, ' ');
3246 if (!c)
3247 continue;
3248 l = snprintf(cache, cache_size, "cpu%d%s", curcpu, c);
3249 if (l < 0) {
3250 perror("Error writing to cache");
3251 rv = 0;
3252 goto err;
3253
3254 }
3255 if (l >= cache_size) {
3256 fprintf(stderr, "Internal error: truncated write to cache\n");
3257 rv = 0;
3258 goto err;
3259 }
3260
3261 cache += l;
3262 cache_size -= l;
3263 total_len += l;
3264
3265 if (sscanf(line, "%*s %lu %lu %lu %lu %lu %lu %lu %lu %lu", &user, &nice, &system, &idle, &iowait, &irq,
3266 &softirq, &steal, &guest) != 9)
3267 continue;
3268 user_sum += user;
3269 nice_sum += nice;
3270 system_sum += system;
3271 idle_sum += idle;
3272 iowait_sum += iowait;
3273 irq_sum += irq;
3274 softirq_sum += softirq;
3275 steal_sum += steal;
3276 guest_sum += guest;
3277 }
3278
3279 cache = d->buf;
3280
3281 int cpuall_len = snprintf(cpuall, CPUALL_MAX_SIZE, "%s %lu %lu %lu %lu %lu %lu %lu %lu %lu\n",
3282 "cpu ", user_sum, nice_sum, system_sum, idle_sum, iowait_sum, irq_sum, softirq_sum, steal_sum, guest_sum);
3283 if (cpuall_len > 0 && cpuall_len < CPUALL_MAX_SIZE){
3284 memcpy(cache, cpuall, cpuall_len);
3285 cache += cpuall_len;
3286 } else{
3287 /* shouldn't happen */
3288 fprintf(stderr, "proc_stat_read copy cpuall failed, cpuall_len=%d\n", cpuall_len);
3289 cpuall_len = 0;
3290 }
3291
3292 memmove(cache, d->buf + CPUALL_MAX_SIZE, total_len);
3293 total_len += cpuall_len;
3294 d->cached = 1;
3295 d->size = total_len;
3296 if (total_len > size ) total_len = size;
3297
3298 memcpy(buf, d->buf, total_len);
3299 rv = total_len;
3300
3301err:
3302 if (f)
3303 fclose(f);
3304 free(line);
3305 free(cpuset);
3306 free(cg);
3307 return rv;
3308}
3309
3310static long int getreaperage(pid_t pid)
3311{
3312 char fnam[100];
3313 struct stat sb;
3314 int ret;
3315 pid_t qpid;
3316
3317 qpid = lookup_initpid_in_store(pid);
3318 if (qpid <= 0)
3319 return 0;
3320
3321 ret = snprintf(fnam, 100, "/proc/%d", qpid);
3322 if (ret < 0 || ret >= 100)
3323 return 0;
3324
3325 if (lstat(fnam, &sb) < 0)
3326 return 0;
3327
3328 return time(NULL) - sb.st_ctime;
3329}
3330
3331static unsigned long get_reaper_busy(pid_t task)
3332{
3333 pid_t initpid = lookup_initpid_in_store(task);
3334 char *cgroup = NULL, *usage_str = NULL;
3335 unsigned long usage = 0;
3336
3337 if (initpid <= 0)
3338 return 0;
3339
3340 cgroup = get_pid_cgroup(initpid, "cpuacct");
3341 if (!cgroup)
3342 goto out;
3343 if (!cgfs_get_value("cpuacct", cgroup, "cpuacct.usage", &usage_str))
3344 goto out;
3345 usage = strtoul(usage_str, NULL, 10);
3346 usage /= 1000000000;
3347
3348out:
3349 free(cgroup);
3350 free(usage_str);
3351 return usage;
3352}
3353
3354#if RELOADTEST
3355void iwashere(void)
3356{
3357 char *name, *cwd = get_current_dir_name();
3358 size_t len;
3359 int fd;
3360
3361 if (!cwd)
3362 exit(1);
3363 len = strlen(cwd) + strlen("/iwashere") + 1;
3364 name = alloca(len);
3365 snprintf(name, len, "%s/iwashere", cwd);
3366 free(cwd);
3367 fd = creat(name, 0755);
3368 if (fd >= 0)
3369 close(fd);
3370}
3371#endif
3372
3373/*
3374 * We read /proc/uptime and reuse its second field.
3375 * For the first field, we use the mtime for the reaper for
3376 * the calling pid as returned by getreaperage
3377 */
3378static int proc_uptime_read(char *buf, size_t size, off_t offset,
3379 struct fuse_file_info *fi)
3380{
3381 struct fuse_context *fc = fuse_get_context();
3382 struct file_info *d = (struct file_info *)fi->fh;
3383 long int reaperage = getreaperage(fc->pid);
3384 unsigned long int busytime = get_reaper_busy(fc->pid), idletime;
3385 char *cache = d->buf;
3386 size_t total_len = 0;
3387
3388#if RELOADTEST
3389 iwashere();
3390#endif
3391
3392 if (offset){
3393 if (offset > d->size)
3394 return -EINVAL;
3395 if (!d->cached)
3396 return 0;
3397 int left = d->size - offset;
3398 total_len = left > size ? size: left;
3399 memcpy(buf, cache + offset, total_len);
3400 return total_len;
3401 }
3402
3403 idletime = reaperage - busytime;
3404 if (idletime > reaperage)
3405 idletime = reaperage;
3406
3407 total_len = snprintf(d->buf, d->size, "%ld.0 %lu.0\n", reaperage, idletime);
3408 if (total_len < 0){
3409 perror("Error writing to cache");
3410 return 0;
3411 }
3412
3413 d->size = (int)total_len;
3414 d->cached = 1;
3415
3416 if (total_len > size) total_len = size;
3417
3418 memcpy(buf, d->buf, total_len);
3419 return total_len;
3420}
3421
3422static int proc_diskstats_read(char *buf, size_t size, off_t offset,
3423 struct fuse_file_info *fi)
3424{
3425 char dev_name[72];
3426 struct fuse_context *fc = fuse_get_context();
3427 struct file_info *d = (struct file_info *)fi->fh;
3428 char *cg;
3429 char *io_serviced_str = NULL, *io_merged_str = NULL, *io_service_bytes_str = NULL,
3430 *io_wait_time_str = NULL, *io_service_time_str = NULL;
3431 unsigned long read = 0, write = 0;
3432 unsigned long read_merged = 0, write_merged = 0;
3433 unsigned long read_sectors = 0, write_sectors = 0;
3434 unsigned long read_ticks = 0, write_ticks = 0;
3435 unsigned long ios_pgr = 0, tot_ticks = 0, rq_ticks = 0;
3436 unsigned long rd_svctm = 0, wr_svctm = 0, rd_wait = 0, wr_wait = 0;
3437 char *cache = d->buf;
3438 size_t cache_size = d->buflen;
3439 char *line = NULL;
3440 size_t linelen = 0, total_len = 0, rv = 0;
3441 unsigned int major = 0, minor = 0;
3442 int i = 0;
3443 FILE *f = NULL;
3444
3445 if (offset){
3446 if (offset > d->size)
3447 return -EINVAL;
3448 if (!d->cached)
3449 return 0;
3450 int left = d->size - offset;
3451 total_len = left > size ? size: left;
3452 memcpy(buf, cache + offset, total_len);
3453 return total_len;
3454 }
3455
3456 pid_t initpid = lookup_initpid_in_store(fc->pid);
3457 if (initpid <= 0)
3458 initpid = fc->pid;
3459 cg = get_pid_cgroup(initpid, "blkio");
3460 if (!cg)
3461 return read_file("/proc/diskstats", buf, size, d);
3462
3463 if (!cgfs_get_value("blkio", cg, "blkio.io_serviced", &io_serviced_str))
3464 goto err;
3465 if (!cgfs_get_value("blkio", cg, "blkio.io_merged", &io_merged_str))
3466 goto err;
3467 if (!cgfs_get_value("blkio", cg, "blkio.io_service_bytes", &io_service_bytes_str))
3468 goto err;
3469 if (!cgfs_get_value("blkio", cg, "blkio.io_wait_time", &io_wait_time_str))
3470 goto err;
3471 if (!cgfs_get_value("blkio", cg, "blkio.io_service_time", &io_service_time_str))
3472 goto err;
3473
3474
3475 f = fopen("/proc/diskstats", "r");
3476 if (!f)
3477 goto err;
3478
3479 while (getline(&line, &linelen, f) != -1) {
3480 size_t l;
3481 char *printme, lbuf[256];
3482
3483 i = sscanf(line, "%u %u %71s", &major, &minor, dev_name);
3484 if(i == 3){
3485 get_blkio_io_value(io_serviced_str, major, minor, "Read", &read);
3486 get_blkio_io_value(io_serviced_str, major, minor, "Write", &write);
3487 get_blkio_io_value(io_merged_str, major, minor, "Read", &read_merged);
3488 get_blkio_io_value(io_merged_str, major, minor, "Write", &write_merged);
3489 get_blkio_io_value(io_service_bytes_str, major, minor, "Read", &read_sectors);
3490 read_sectors = read_sectors/512;
3491 get_blkio_io_value(io_service_bytes_str, major, minor, "Write", &write_sectors);
3492 write_sectors = write_sectors/512;
3493
3494 get_blkio_io_value(io_service_time_str, major, minor, "Read", &rd_svctm);
3495 rd_svctm = rd_svctm/1000000;
3496 get_blkio_io_value(io_wait_time_str, major, minor, "Read", &rd_wait);
3497 rd_wait = rd_wait/1000000;
3498 read_ticks = rd_svctm + rd_wait;
3499
3500 get_blkio_io_value(io_service_time_str, major, minor, "Write", &wr_svctm);
3501 wr_svctm = wr_svctm/1000000;
3502 get_blkio_io_value(io_wait_time_str, major, minor, "Write", &wr_wait);
3503 wr_wait = wr_wait/1000000;
3504 write_ticks = wr_svctm + wr_wait;
3505
3506 get_blkio_io_value(io_service_time_str, major, minor, "Total", &tot_ticks);
3507 tot_ticks = tot_ticks/1000000;
3508 }else{
3509 continue;
3510 }
3511
3512 memset(lbuf, 0, 256);
3513 if (read || write || read_merged || write_merged || read_sectors || write_sectors || read_ticks || write_ticks) {
3514 snprintf(lbuf, 256, "%u %u %s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu\n",
3515 major, minor, dev_name, read, read_merged, read_sectors, read_ticks,
3516 write, write_merged, write_sectors, write_ticks, ios_pgr, tot_ticks, rq_ticks);
3517 printme = lbuf;
3518 } else
3519 continue;
3520
3521 l = snprintf(cache, cache_size, "%s", printme);
3522 if (l < 0) {
3523 perror("Error writing to fuse buf");
3524 rv = 0;
3525 goto err;
3526 }
3527 if (l >= cache_size) {
3528 fprintf(stderr, "Internal error: truncated write to cache\n");
3529 rv = 0;
3530 goto err;
3531 }
3532 cache += l;
3533 cache_size -= l;
3534 total_len += l;
3535 }
3536
3537 d->cached = 1;
3538 d->size = total_len;
3539 if (total_len > size ) total_len = size;
3540 memcpy(buf, d->buf, total_len);
3541
3542 rv = total_len;
3543err:
3544 free(cg);
3545 if (f)
3546 fclose(f);
3547 free(line);
3548 free(io_serviced_str);
3549 free(io_merged_str);
3550 free(io_service_bytes_str);
3551 free(io_wait_time_str);
3552 free(io_service_time_str);
3553 return rv;
3554}
3555
3556static off_t get_procfile_size(const char *which)
3557{
3558 FILE *f = fopen(which, "r");
3559 char *line = NULL;
3560 size_t len = 0;
3561 ssize_t sz, answer = 0;
3562 if (!f)
3563 return 0;
3564
3565 while ((sz = getline(&line, &len, f)) != -1)
3566 answer += sz;
3567 fclose (f);
3568 free(line);
3569
3570 return answer;
3571}
3572
3573int proc_getattr(const char *path, struct stat *sb)
3574{
3575 struct timespec now;
3576
3577 memset(sb, 0, sizeof(struct stat));
3578 if (clock_gettime(CLOCK_REALTIME, &now) < 0)
3579 return -EINVAL;
3580 sb->st_uid = sb->st_gid = 0;
3581 sb->st_atim = sb->st_mtim = sb->st_ctim = now;
3582 if (strcmp(path, "/proc") == 0) {
3583 sb->st_mode = S_IFDIR | 00555;
3584 sb->st_nlink = 2;
3585 return 0;
3586 }
3587 if (strcmp(path, "/proc/meminfo") == 0 ||
3588 strcmp(path, "/proc/cpuinfo") == 0 ||
3589 strcmp(path, "/proc/uptime") == 0 ||
3590 strcmp(path, "/proc/stat") == 0 ||
3591 strcmp(path, "/proc/diskstats") == 0) {
3592 sb->st_size = 0;
3593 sb->st_mode = S_IFREG | 00444;
3594 sb->st_nlink = 1;
3595 return 0;
3596 }
3597
3598 return -ENOENT;
3599}
3600
3601int proc_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset,
3602 struct fuse_file_info *fi)
3603{
3604 if (filler(buf, "cpuinfo", NULL, 0) != 0 ||
3605 filler(buf, "meminfo", NULL, 0) != 0 ||
3606 filler(buf, "stat", NULL, 0) != 0 ||
3607 filler(buf, "uptime", NULL, 0) != 0 ||
3608 filler(buf, "diskstats", NULL, 0) != 0)
3609 return -EINVAL;
3610 return 0;
3611}
3612
3613int proc_open(const char *path, struct fuse_file_info *fi)
3614{
3615 int type = -1;
3616 struct file_info *info;
3617
3618 if (strcmp(path, "/proc/meminfo") == 0)
3619 type = LXC_TYPE_PROC_MEMINFO;
3620 else if (strcmp(path, "/proc/cpuinfo") == 0)
3621 type = LXC_TYPE_PROC_CPUINFO;
3622 else if (strcmp(path, "/proc/uptime") == 0)
3623 type = LXC_TYPE_PROC_UPTIME;
3624 else if (strcmp(path, "/proc/stat") == 0)
3625 type = LXC_TYPE_PROC_STAT;
3626 else if (strcmp(path, "/proc/diskstats") == 0)
3627 type = LXC_TYPE_PROC_DISKSTATS;
3628 if (type == -1)
3629 return -ENOENT;
3630
3631 info = malloc(sizeof(*info));
3632 if (!info)
3633 return -ENOMEM;
3634
3635 memset(info, 0, sizeof(*info));
3636 info->type = type;
3637
3638 info->buflen = get_procfile_size(path) + BUF_RESERVE_SIZE;
3639 do {
3640 info->buf = malloc(info->buflen);
3641 } while (!info->buf);
3642 memset(info->buf, 0, info->buflen);
3643 /* set actual size to buffer size */
3644 info->size = info->buflen;
3645
3646 fi->fh = (unsigned long)info;
3647 return 0;
3648}
3649
3650int proc_release(const char *path, struct fuse_file_info *fi)
3651{
3652 struct file_info *f = (struct file_info *)fi->fh;
3653
3654 do_release_file_info(f);
3655 return 0;
3656}
3657
3658int proc_read(const char *path, char *buf, size_t size, off_t offset,
3659 struct fuse_file_info *fi)
3660{
3661 struct file_info *f = (struct file_info *) fi->fh;
3662
3663 switch (f->type) {
3664 case LXC_TYPE_PROC_MEMINFO:
3665 return proc_meminfo_read(buf, size, offset, fi);
3666 case LXC_TYPE_PROC_CPUINFO:
3667 return proc_cpuinfo_read(buf, size, offset, fi);
3668 case LXC_TYPE_PROC_UPTIME:
3669 return proc_uptime_read(buf, size, offset, fi);
3670 case LXC_TYPE_PROC_STAT:
3671 return proc_stat_read(buf, size, offset, fi);
3672 case LXC_TYPE_PROC_DISKSTATS:
3673 return proc_diskstats_read(buf, size, offset, fi);
3674 default:
3675 return -EINVAL;
3676 }
3677}
3678
3679static void __attribute__((constructor)) collect_subsystems(void)
3680{
3681 FILE *f;
3682 char *line = NULL;
3683 size_t len = 0;
3684
3685 if ((f = fopen("/proc/self/cgroup", "r")) == NULL) {
3686 fprintf(stderr, "Error opening /proc/self/cgroup: %s\n", strerror(errno));
3687 return;
3688 }
3689 while (getline(&line, &len, f) != -1) {
3690 char *p, *p2;
3691
3692 p = strchr(line, ':');
3693 if (!p)
3694 goto out;
3695 *(p++) = '\0';
3696
3697 p2 = strrchr(p, ':');
3698 if (!p2)
3699 goto out;
3700 *p2 = '\0';
3701
3702 if (!store_hierarchy(line, p))
3703 goto out;
3704 }
3705
3706 print_subsystems();
3707
3708out:
3709 free(line);
3710 fclose(f);
3711}
3712
3713static void __attribute__((destructor)) free_subsystems(void)
3714{
3715 int i;
3716
3717 for (i = 0; i < num_hierarchies; i++)
3718 if (hierarchies[i])
3719 free(hierarchies[i]);
3720 free(hierarchies);
3721}