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