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