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