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