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