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