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