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