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