]> git.proxmox.com Git - mirror_lxcfs.git/blame - lxcfs.c
skip empty entries under /proc/self/cgroup
[mirror_lxcfs.git] / lxcfs.c
CommitLineData
758ad80c
SH
1/* lxcfs
2 *
b11c6ec0 3 * Copyright © 2014-2016 Canonical, Inc
758ad80c
SH
4 * Author: Serge Hallyn <serge.hallyn@ubuntu.com>
5 *
f2799430 6 * See COPYING file for details.
758ad80c
SH
7 */
8
758ad80c
SH
9#define FUSE_USE_VERSION 26
10
2183082c 11#include <stdio.h>
758ad80c
SH
12#include <dirent.h>
13#include <fcntl.h>
14#include <fuse.h>
15#include <unistd.h>
16#include <errno.h>
17#include <stdbool.h>
18#include <time.h>
19#include <string.h>
20#include <stdlib.h>
21#include <libgen.h>
41bb9357 22#include <sched.h>
b11c6ec0 23#include <pthread.h>
237e200e 24#include <dlfcn.h>
41bb9357 25#include <linux/sched.h>
a05660a6 26#include <sys/socket.h>
41bb9357 27#include <sys/mount.h>
5b2dfd85 28#include <sys/epoll.h>
41bb9357 29#include <wait.h>
758ad80c 30
2e9c0b32 31#include "config.h" // for VERSION
237e200e 32#include "bindings.h"
758ad80c 33
237e200e 34void *dlopen_handle;
b11c6ec0 35
237e200e 36/* Functions to keep track of number of threads using the library */
6d1308cb 37
237e200e
SH
38static int users_count;
39static pthread_mutex_t user_count_mutex = PTHREAD_MUTEX_INITIALIZER;
b11c6ec0 40static void lock_mutex(pthread_mutex_t *l)
2c51f8dd 41{
b11c6ec0 42 int ret;
2c51f8dd 43
b11c6ec0
SH
44 if ((ret = pthread_mutex_lock(l)) != 0) {
45 fprintf(stderr, "pthread_mutex_lock returned:%d %s\n", ret, strerror(ret));
46 exit(1);
47 }
48}
2c51f8dd 49
237e200e 50static void unlock_mutex(pthread_mutex_t *l)
23ce2127 51{
237e200e 52 int ret;
2dc17609 53
237e200e
SH
54 if ((ret = pthread_mutex_unlock(l)) != 0) {
55 fprintf(stderr, "pthread_mutex_unlock returned:%d %s\n", ret, strerror(ret));
56 exit(1);
97f1f27b 57 }
237e200e 58}
2dc17609 59
237e200e
SH
60static void users_lock(void)
61{
62 lock_mutex(&user_count_mutex);
63}
b731895e 64
237e200e
SH
65static void users_unlock(void)
66{
67 unlock_mutex(&user_count_mutex);
68}
e1068397 69
81d17d8f 70static volatile sig_atomic_t need_reload;
59120f04 71
237e200e
SH
72/* do_reload - reload the dynamic library. Done under
73 * lock and when we know the user_count was 0 */
74static void do_reload(void)
75{
76 if (dlopen_handle)
77 dlclose(dlopen_handle);
e1068397 78
59120f04 79 /* First try loading using ld.so */
237e200e 80 dlopen_handle = dlopen("liblxcfs.so", RTLD_LAZY);
59120f04
SH
81 if (dlopen_handle)
82 goto good;
83
7c82d95e 84 dlopen_handle = dlopen("/usr/lib/lxcfs/liblxcfs.so", RTLD_LAZY);
237e200e
SH
85 if (!dlopen_handle) {
86 fprintf(stderr, "Failed to open liblxcfs\n");
87 _exit(1);
2dc17609 88 }
59120f04
SH
89
90good:
91 if (need_reload)
92 fprintf(stderr, "lxcfs: reloaded\n");
237e200e 93 need_reload = 0;
23ce2127
SH
94}
95
237e200e 96static void up_users(void)
23ce2127 97{
237e200e
SH
98 users_lock();
99 if (users_count == 0 && need_reload)
100 do_reload();
101 users_count++;
102 users_unlock();
103}
23ce2127 104
237e200e
SH
105static void down_users(void)
106{
107 users_lock();
108 users_count--;
109 users_unlock();
23ce2127
SH
110}
111
237e200e
SH
112static void reload_handler(int sig)
113{
237e200e 114 need_reload = 1;
237e200e 115}
23ce2127 116
237e200e
SH
117/* Functions to run the library methods */
118static int do_cg_getattr(const char *path, struct stat *sb)
aeb56147 119{
237e200e
SH
120 int (*cg_getattr)(const char *path, struct stat *sb);
121 char *error;
122 dlerror(); /* Clear any existing error */
123 cg_getattr = (int (*)(const char *, struct stat *)) dlsym(dlopen_handle, "cg_getattr");
124 error = dlerror();
125 if (error != NULL) {
126 fprintf(stderr, "cg_getattr: %s\n", error);
127 return -1;
128 }
aeb56147 129
237e200e 130 return cg_getattr(path, sb);
aeb56147
SH
131}
132
237e200e 133static int do_proc_getattr(const char *path, struct stat *sb)
23ce2127 134{
237e200e
SH
135 int (*proc_getattr)(const char *path, struct stat *sb);
136 char *error;
137 dlerror(); /* Clear any existing error */
138 proc_getattr = (int (*)(const char *, struct stat *)) dlsym(dlopen_handle, "proc_getattr");
139 error = dlerror();
140 if (error != NULL) {
141 fprintf(stderr, "proc_getattr: %s\n", error);
142 return -1;
143 }
23ce2127 144
237e200e 145 return proc_getattr(path, sb);
23ce2127
SH
146}
147
237e200e 148static int do_cg_read(const char *path, char *buf, size_t size, off_t offset,
23ce2127
SH
149 struct fuse_file_info *fi)
150{
237e200e
SH
151 int (*cg_read)(const char *path, char *buf, size_t size, off_t offset,
152 struct fuse_file_info *fi);
153 char *error;
23ce2127 154
237e200e
SH
155 dlerror(); /* Clear any existing error */
156 cg_read = (int (*)(const char *, char *, size_t, off_t, struct fuse_file_info *)) dlsym(dlopen_handle, "cg_read");
157 error = dlerror();
158 if (error != NULL) {
159 fprintf(stderr, "cg_read: %s\n", error);
160 return -1;
23ce2127
SH
161 }
162
237e200e 163 return cg_read(path, buf, size, offset, fi);
23ce2127
SH
164}
165
237e200e 166static int do_proc_read(const char *path, char *buf, size_t size, off_t offset,
23ce2127
SH
167 struct fuse_file_info *fi)
168{
237e200e
SH
169 int (*proc_read)(const char *path, char *buf, size_t size, off_t offset,
170 struct fuse_file_info *fi);
171 char *error;
aeb56147 172
237e200e
SH
173 dlerror(); /* Clear any existing error */
174 proc_read = (int (*)(const char *, char *, size_t, off_t, struct fuse_file_info *)) dlsym(dlopen_handle, "proc_read");
175 error = dlerror();
176 if (error != NULL) {
177 fprintf(stderr, "proc_read: %s\n", error);
178 return -1;
97f1f27b
YY
179 }
180
237e200e
SH
181 return proc_read(path, buf, size, offset, fi);
182}
97f1f27b 183
237e200e
SH
184static int do_cg_write(const char *path, const char *buf, size_t size, off_t offset,
185 struct fuse_file_info *fi)
186{
187 int (*cg_write)(const char *path, const char *buf, size_t size, off_t offset,
188 struct fuse_file_info *fi);
189 char *error;
190 dlerror(); /* Clear any existing error */
191 cg_write = (int (*)(const char *, const char *, size_t, off_t, struct fuse_file_info *)) dlsym(dlopen_handle, "cg_write");
192 error = dlerror();
193 if (error != NULL) {
194 fprintf(stderr, "cg_write: %s\n", error);
195 return -1;
97f1f27b
YY
196 }
197
237e200e 198 return cg_write(path, buf, size, offset, fi);
23ce2127
SH
199}
200
237e200e 201static int do_cg_mkdir(const char *path, mode_t mode)
41bb9357 202{
237e200e
SH
203 int (*cg_mkdir)(const char *path, mode_t mode);
204 char *error;
205 dlerror(); /* Clear any existing error */
206 cg_mkdir = (int (*)(const char *, mode_t)) dlsym(dlopen_handle, "cg_mkdir");
207 error = dlerror();
208 if (error != NULL) {
209 fprintf(stderr, "cg_mkdir: %s\n", error);
210 return -1;
211 }
41bb9357 212
237e200e 213 return cg_mkdir(path, mode);
41bb9357
SH
214}
215
237e200e 216static int do_cg_chown(const char *path, uid_t uid, gid_t gid)
0b6af11b 217{
237e200e
SH
218 int (*cg_chown)(const char *path, uid_t uid, gid_t gid);
219 char *error;
220 dlerror(); /* Clear any existing error */
221 cg_chown = (int (*)(const char *, uid_t, gid_t)) dlsym(dlopen_handle, "cg_chown");
222 error = dlerror();
223 if (error != NULL) {
224 fprintf(stderr, "cg_chown: %s\n", error);
225 return -1;
226 }
0b6af11b 227
237e200e 228 return cg_chown(path, uid, gid);
41bb9357
SH
229}
230
237e200e 231static int do_cg_rmdir(const char *path)
23ce2127 232{
237e200e
SH
233 int (*cg_rmdir)(const char *path);
234 char *error;
235 dlerror(); /* Clear any existing error */
236 cg_rmdir = (int (*)(const char *path)) dlsym(dlopen_handle, "cg_rmdir");
237 error = dlerror();
238 if (error != NULL) {
239 fprintf(stderr, "cg_rmdir: %s\n", error);
240 return -1;
97f1f27b
YY
241 }
242
237e200e
SH
243 return cg_rmdir(path);
244}
f6c0b279 245
237e200e
SH
246static int do_cg_chmod(const char *path, mode_t mode)
247{
248 int (*cg_chmod)(const char *path, mode_t mode);
249 char *error;
250 dlerror(); /* Clear any existing error */
251 cg_chmod = (int (*)(const char *, mode_t)) dlsym(dlopen_handle, "cg_chmod");
252 error = dlerror();
253 if (error != NULL) {
254 fprintf(stderr, "cg_chmod: %s\n", error);
255 return -1;
e1068397 256 }
cdcdb29b 257
237e200e 258 return cg_chmod(path, mode);
23ce2127
SH
259}
260
237e200e 261static int do_cg_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset,
49878439
YY
262 struct fuse_file_info *fi)
263{
237e200e
SH
264 int (*cg_readdir)(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset,
265 struct fuse_file_info *fi);
266 char *error;
49878439 267
237e200e
SH
268 dlerror(); /* Clear any existing error */
269 cg_readdir = (int (*)(const char *, void *, fuse_fill_dir_t, off_t, struct fuse_file_info *)) dlsym(dlopen_handle, "cg_readdir");
270 error = dlerror();
271 if (error != NULL) {
272 fprintf(stderr, "cg_readdir: %s\n", error);
273 return -1;
97f1f27b 274 }
49878439 275
237e200e
SH
276 return cg_readdir(path, buf, filler, offset, fi);
277}
49878439 278
237e200e
SH
279static int do_proc_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset,
280 struct fuse_file_info *fi)
281{
282 int (*proc_readdir)(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset,
283 struct fuse_file_info *fi);
284 char *error;
49878439 285
237e200e
SH
286 dlerror(); /* Clear any existing error */
287 proc_readdir = (int (*)(const char *, void *, fuse_fill_dir_t, off_t, struct fuse_file_info *)) dlsym(dlopen_handle, "proc_readdir");
288 error = dlerror();
289 if (error != NULL) {
290 fprintf(stderr, "proc_readdir: %s\n", error);
291 return -1;
49878439
YY
292 }
293
237e200e 294 return proc_readdir(path, buf, filler, offset, fi);
49878439
YY
295}
296
237e200e 297static int do_cg_open(const char *path, struct fuse_file_info *fi)
23ce2127 298{
237e200e
SH
299 int (*cg_open)(const char *path, struct fuse_file_info *fi);
300 char *error;
301 dlerror(); /* Clear any existing error */
302 cg_open = (int (*)(const char *, struct fuse_file_info *)) dlsym(dlopen_handle, "cg_open");
303 error = dlerror();
304 if (error != NULL) {
305 fprintf(stderr, "cg_open: %s\n", error);
306 return -1;
307 }
23ce2127 308
237e200e 309 return cg_open(path, fi);
23ce2127
SH
310}
311
bddbb106
SH
312static int do_cg_access(const char *path, int mode)
313{
314 int (*cg_access)(const char *path, int mode);
315 char *error;
316 dlerror(); /* Clear any existing error */
317 cg_access = (int (*)(const char *, int mode)) dlsym(dlopen_handle, "cg_access");
318 error = dlerror();
319 if (error != NULL) {
320 fprintf(stderr, "cg_access: %s\n", error);
321 return -1;
322 }
323
324 return cg_access(path, mode);
325}
326
237e200e 327static int do_proc_open(const char *path, struct fuse_file_info *fi)
758ad80c 328{
237e200e
SH
329 int (*proc_open)(const char *path, struct fuse_file_info *fi);
330 char *error;
331 dlerror(); /* Clear any existing error */
332 proc_open = (int (*)(const char *path, struct fuse_file_info *fi)) dlsym(dlopen_handle, "proc_open");
333 error = dlerror();
334 if (error != NULL) {
335 fprintf(stderr, "proc_open: %s\n", error);
336 return -1;
35629743
SH
337 }
338
237e200e 339 return proc_open(path, fi);
35629743
SH
340}
341
bddbb106
SH
342static int do_proc_access(const char *path, int mode)
343{
344 int (*proc_access)(const char *path, int mode);
345 char *error;
346 dlerror(); /* Clear any existing error */
347 proc_access = (int (*)(const char *, int mode)) dlsym(dlopen_handle, "proc_access");
348 error = dlerror();
349 if (error != NULL) {
350 fprintf(stderr, "proc_access: %s\n", error);
351 return -1;
352 }
353
354 return proc_access(path, mode);
355}
356
237e200e 357static int do_cg_release(const char *path, struct fuse_file_info *fi)
35629743 358{
237e200e
SH
359 int (*cg_release)(const char *path, struct fuse_file_info *fi);
360 char *error;
361 dlerror(); /* Clear any existing error */
362 cg_release = (int (*)(const char *path, struct fuse_file_info *)) dlsym(dlopen_handle, "cg_release");
363 error = dlerror();
364 if (error != NULL) {
365 fprintf(stderr, "cg_release: %s\n", error);
366 return -1;
367 }
368
369 return cg_release(path, fi);
758ad80c
SH
370}
371
237e200e 372static int do_proc_release(const char *path, struct fuse_file_info *fi)
35629743 373{
237e200e
SH
374 int (*proc_release)(const char *path, struct fuse_file_info *fi);
375 char *error;
376 dlerror(); /* Clear any existing error */
377 proc_release = (int (*)(const char *path, struct fuse_file_info *)) dlsym(dlopen_handle, "proc_release");
378 error = dlerror();
379 if (error != NULL) {
380 fprintf(stderr, "proc_release: %s\n", error);
381 return -1;
382 }
97f1f27b 383
237e200e 384 return proc_release(path, fi);
96fc5ee6
SH
385}
386
237e200e 387static int do_cg_opendir(const char *path, struct fuse_file_info *fi)
96fc5ee6 388{
237e200e
SH
389 int (*cg_opendir)(const char *path, struct fuse_file_info *fi);
390 char *error;
391 dlerror(); /* Clear any existing error */
392 cg_opendir = (int (*)(const char *path, struct fuse_file_info *fi)) dlsym(dlopen_handle, "cg_opendir");
393 error = dlerror();
394 if (error != NULL) {
395 fprintf(stderr, "cg_opendir: %s\n", error);
396 return -1;
397 }
96fc5ee6 398
237e200e 399 return cg_opendir(path, fi);
35629743
SH
400}
401
237e200e 402static int do_cg_releasedir(const char *path, struct fuse_file_info *fi)
35629743 403{
237e200e
SH
404 int (*cg_releasedir)(const char *path, struct fuse_file_info *fi);
405 char *error;
406 dlerror(); /* Clear any existing error */
407 cg_releasedir = (int (*)(const char *path, struct fuse_file_info *)) dlsym(dlopen_handle, "cg_releasedir");
408 error = dlerror();
409 if (error != NULL) {
410 fprintf(stderr, "cg_releasedir: %s\n", error);
411 return -1;
96fc5ee6 412 }
237e200e
SH
413
414 return cg_releasedir(path, fi);
35629743
SH
415}
416
2ad6d2bd
SH
417/*
418 * FUSE ops for /
419 * these just delegate to the /proc and /cgroup ops as
420 * needed
421 */
758ad80c
SH
422
423static int lxcfs_getattr(const char *path, struct stat *sb)
424{
237e200e 425 int ret;
758ad80c
SH
426 if (strcmp(path, "/") == 0) {
427 sb->st_mode = S_IFDIR | 00755;
428 sb->st_nlink = 2;
429 return 0;
430 }
431 if (strncmp(path, "/cgroup", 7) == 0) {
237e200e
SH
432 up_users();
433 ret = do_cg_getattr(path, sb);
434 down_users();
435 return ret;
758ad80c 436 }
35629743 437 if (strncmp(path, "/proc", 5) == 0) {
237e200e
SH
438 up_users();
439 ret = do_proc_getattr(path, sb);
440 down_users();
441 return ret;
758ad80c
SH
442 }
443 return -EINVAL;
444}
445
446static int lxcfs_opendir(const char *path, struct fuse_file_info *fi)
447{
237e200e 448 int ret;
758ad80c
SH
449 if (strcmp(path, "/") == 0)
450 return 0;
451
452 if (strncmp(path, "/cgroup", 7) == 0) {
237e200e
SH
453 up_users();
454 ret = do_cg_opendir(path, fi);
455 down_users();
456 return ret;
758ad80c 457 }
35629743
SH
458 if (strcmp(path, "/proc") == 0)
459 return 0;
460 return -ENOENT;
758ad80c
SH
461}
462
463static int lxcfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset,
464 struct fuse_file_info *fi)
465{
237e200e 466 int ret;
758ad80c
SH
467 if (strcmp(path, "/") == 0) {
468 if (filler(buf, "proc", NULL, 0) != 0 ||
469 filler(buf, "cgroup", NULL, 0) != 0)
470 return -EINVAL;
471 return 0;
472 }
237e200e
SH
473 if (strncmp(path, "/cgroup", 7) == 0) {
474 up_users();
475 ret = do_cg_readdir(path, buf, filler, offset, fi);
476 down_users();
477 return ret;
478 }
479 if (strcmp(path, "/proc") == 0) {
480 up_users();
481 ret = do_proc_readdir(path, buf, filler, offset, fi);
482 down_users();
483 return ret;
484 }
758ad80c
SH
485 return -EINVAL;
486}
487
bddbb106
SH
488static int lxcfs_access(const char *path, int mode)
489{
490 int ret;
491 if (strncmp(path, "/cgroup", 7) == 0) {
492 up_users();
493 ret = do_cg_access(path, mode);
494 down_users();
495 return ret;
496 }
497 if (strncmp(path, "/proc", 5) == 0) {
498 up_users();
499 ret = do_proc_access(path, mode);
500 down_users();
501 return ret;
502 }
503
504 return -EINVAL;
505}
506
758ad80c
SH
507static int lxcfs_releasedir(const char *path, struct fuse_file_info *fi)
508{
237e200e 509 int ret;
758ad80c
SH
510 if (strcmp(path, "/") == 0)
511 return 0;
512 if (strncmp(path, "/cgroup", 7) == 0) {
237e200e
SH
513 up_users();
514 ret = do_cg_releasedir(path, fi);
515 down_users();
516 return ret;
758ad80c 517 }
35629743
SH
518 if (strcmp(path, "/proc") == 0)
519 return 0;
758ad80c
SH
520 return -EINVAL;
521}
522
99978832
SH
523static int lxcfs_open(const char *path, struct fuse_file_info *fi)
524{
237e200e
SH
525 int ret;
526 if (strncmp(path, "/cgroup", 7) == 0) {
527 up_users();
528 ret = do_cg_open(path, fi);
529 down_users();
530 return ret;
531 }
532 if (strncmp(path, "/proc", 5) == 0) {
533 up_users();
534 ret = do_proc_open(path, fi);
535 down_users();
536 return ret;
537 }
99978832
SH
538
539 return -EINVAL;
540}
541
542static int lxcfs_read(const char *path, char *buf, size_t size, off_t offset,
543 struct fuse_file_info *fi)
544{
237e200e
SH
545 int ret;
546 if (strncmp(path, "/cgroup", 7) == 0) {
547 up_users();
548 ret = do_cg_read(path, buf, size, offset, fi);
549 down_users();
550 return ret;
551 }
552 if (strncmp(path, "/proc", 5) == 0) {
553 up_users();
554 ret = do_proc_read(path, buf, size, offset, fi);
555 down_users();
556 return ret;
557 }
99978832
SH
558
559 return -EINVAL;
560}
561
2ad6d2bd
SH
562int lxcfs_write(const char *path, const char *buf, size_t size, off_t offset,
563 struct fuse_file_info *fi)
564{
237e200e 565 int ret;
2ad6d2bd 566 if (strncmp(path, "/cgroup", 7) == 0) {
237e200e
SH
567 up_users();
568 ret = do_cg_write(path, buf, size, offset, fi);
569 down_users();
570 return ret;
2ad6d2bd
SH
571 }
572
573 return -EINVAL;
574}
575
99978832
SH
576static int lxcfs_flush(const char *path, struct fuse_file_info *fi)
577{
578 return 0;
579}
580
581static int lxcfs_release(const char *path, struct fuse_file_info *fi)
758ad80c 582{
237e200e
SH
583 int ret;
584 if (strncmp(path, "/cgroup", 7) == 0) {
585 up_users();
586 ret = do_cg_release(path, fi);
587 down_users();
588 return ret;
589 }
590 if (strncmp(path, "/proc", 5) == 0) {
591 up_users();
592 ret = do_proc_release(path, fi);
593 down_users();
594 return ret;
595 }
8f6e8f5e
SH
596
597 return -EINVAL;
99978832
SH
598}
599
600static int lxcfs_fsync(const char *path, int datasync, struct fuse_file_info *fi)
601{
602 return 0;
758ad80c
SH
603}
604
ab54b798
SH
605int lxcfs_mkdir(const char *path, mode_t mode)
606{
237e200e
SH
607 int ret;
608 if (strncmp(path, "/cgroup", 7) == 0) {
609 up_users();
610 ret = do_cg_mkdir(path, mode);
611 down_users();
612 return ret;
613 }
ab54b798
SH
614
615 return -EINVAL;
616}
617
341b21ad
SH
618int lxcfs_chown(const char *path, uid_t uid, gid_t gid)
619{
237e200e
SH
620 int ret;
621 if (strncmp(path, "/cgroup", 7) == 0) {
622 up_users();
623 ret = do_cg_chown(path, uid, gid);
624 down_users();
625 return ret;
626 }
341b21ad
SH
627
628 return -EINVAL;
629}
630
2ad6d2bd
SH
631/*
632 * cat first does a truncate before doing ops->write. This doesn't
633 * really make sense for cgroups. So just return 0 always but do
634 * nothing.
635 */
636int lxcfs_truncate(const char *path, off_t newsize)
637{
638 if (strncmp(path, "/cgroup", 7) == 0)
639 return 0;
640 return -EINVAL;
641}
642
50d8d5b5
SH
643int lxcfs_rmdir(const char *path)
644{
237e200e
SH
645 int ret;
646 if (strncmp(path, "/cgroup", 7) == 0) {
647 up_users();
648 ret = do_cg_rmdir(path);
649 down_users();
650 return ret;
651 }
50d8d5b5
SH
652 return -EINVAL;
653}
654
fd2e4e03
SH
655int lxcfs_chmod(const char *path, mode_t mode)
656{
237e200e
SH
657 int ret;
658 if (strncmp(path, "/cgroup", 7) == 0) {
659 up_users();
660 ret = do_cg_chmod(path, mode);
661 down_users();
662 return ret;
663 }
fd2e4e03
SH
664 return -EINVAL;
665}
666
758ad80c
SH
667const struct fuse_operations lxcfs_ops = {
668 .getattr = lxcfs_getattr,
669 .readlink = NULL,
670 .getdir = NULL,
671 .mknod = NULL,
ab54b798 672 .mkdir = lxcfs_mkdir,
758ad80c 673 .unlink = NULL,
50d8d5b5 674 .rmdir = lxcfs_rmdir,
758ad80c
SH
675 .symlink = NULL,
676 .rename = NULL,
677 .link = NULL,
fd2e4e03 678 .chmod = lxcfs_chmod,
341b21ad 679 .chown = lxcfs_chown,
2ad6d2bd 680 .truncate = lxcfs_truncate,
758ad80c 681 .utime = NULL,
99978832
SH
682
683 .open = lxcfs_open,
684 .read = lxcfs_read,
685 .release = lxcfs_release,
2ad6d2bd 686 .write = lxcfs_write,
99978832 687
758ad80c 688 .statfs = NULL,
99978832
SH
689 .flush = lxcfs_flush,
690 .fsync = lxcfs_fsync,
758ad80c
SH
691
692 .setxattr = NULL,
693 .getxattr = NULL,
694 .listxattr = NULL,
695 .removexattr = NULL,
696
697 .opendir = lxcfs_opendir,
698 .readdir = lxcfs_readdir,
699 .releasedir = lxcfs_releasedir,
700
701 .fsyncdir = NULL,
702 .init = NULL,
703 .destroy = NULL,
bddbb106 704 .access = lxcfs_access,
758ad80c
SH
705 .create = NULL,
706 .ftruncate = NULL,
707 .fgetattr = NULL,
708};
709
99978832 710static void usage(const char *me)
758ad80c
SH
711{
712 fprintf(stderr, "Usage:\n");
713 fprintf(stderr, "\n");
e190ee91
SH
714 fprintf(stderr, "%s [-p pidfile] mountpoint\n", me);
715 fprintf(stderr, " Default pidfile is %s/lxcfs.pid\n", RUNTIME_PATH);
0b0f73db 716 fprintf(stderr, "%s -h\n", me);
758ad80c
SH
717 exit(1);
718}
719
99978832 720static bool is_help(char *w)
758ad80c
SH
721{
722 if (strcmp(w, "-h") == 0 ||
723 strcmp(w, "--help") == 0 ||
724 strcmp(w, "-help") == 0 ||
725 strcmp(w, "help") == 0)
726 return true;
727 return false;
728}
729
0b0f73db
SH
730void swallow_arg(int *argcp, char *argv[], char *which)
731{
732 int i;
733
734 for (i = 1; argv[i]; i++) {
735 if (strcmp(argv[i], which) != 0)
736 continue;
737 for (; argv[i]; i++) {
738 argv[i] = argv[i+1];
739 }
740 (*argcp)--;
741 return;
742 }
743}
744
e190ee91 745bool swallow_option(int *argcp, char *argv[], char *opt, char **v)
0b0f73db
SH
746{
747 int i;
748
749 for (i = 1; argv[i]; i++) {
750 if (!argv[i+1])
751 continue;
752 if (strcmp(argv[i], opt) != 0)
753 continue;
e190ee91
SH
754 do {
755 *v = strdup(argv[i+1]);
756 } while (!*v);
0b0f73db
SH
757 for (; argv[i+1]; i++) {
758 argv[i] = argv[i+2];
759 }
760 (*argcp) -= 2;
e190ee91 761 return true;
0b0f73db 762 }
e190ee91 763 return false;
0b0f73db
SH
764}
765
237e200e
SH
766static bool mkdir_p(const char *dir, mode_t mode)
767{
768 const char *tmp = dir;
769 const char *orig = dir;
770 char *makeme;
771
772 do {
773 dir = tmp + strspn(tmp, "/");
774 tmp = dir + strcspn(dir, "/");
775 makeme = strndup(orig, dir - orig);
776 if (!makeme)
777 return false;
778 if (mkdir(makeme, mode) && errno != EEXIST) {
779 fprintf(stderr, "failed to create directory '%s': %s",
780 makeme, strerror(errno));
781 free(makeme);
782 return false;
783 }
784 free(makeme);
785 } while(tmp != dir);
786
787 return true;
788}
789
790static bool umount_if_mounted(void)
791{
792 if (umount2(basedir, MNT_DETACH) < 0 && errno != EINVAL) {
793 fprintf(stderr, "failed to umount %s: %s\n", basedir,
794 strerror(errno));
795 return false;
796 }
797 return true;
798}
799
800static bool setup_cgfs_dir(void)
801{
802 if (!mkdir_p(basedir, 0700)) {
803 fprintf(stderr, "Failed to create lxcfs cgdir\n");
804 return false;
805 }
806 if (!umount_if_mounted()) {
807 fprintf(stderr, "Failed to clean up old lxcfs cgdir\n");
808 return false;
809 }
810 if (mount("tmpfs", basedir, "tmpfs", 0, "size=100000,mode=700") < 0) {
811 fprintf(stderr, "Failed to mount tmpfs for private controllers\n");
812 return false;
813 }
814 return true;
815}
816
817static bool do_mount_cgroup(char *controller)
818{
819 char *target;
820 size_t len;
821 int ret;
822
823 len = strlen(basedir) + strlen(controller) + 2;
824 target = alloca(len);
825 ret = snprintf(target, len, "%s/%s", basedir, controller);
826 if (ret < 0 || ret >= len)
827 return false;
828 if (mkdir(target, 0755) < 0 && errno != EEXIST)
829 return false;
830 if (mount(controller, target, "cgroup", 0, controller) < 0) {
831 fprintf(stderr, "Failed mounting cgroup %s\n", controller);
832 return false;
833 }
834 return true;
835}
836
837static bool do_mount_cgroups(void)
838{
a262ddb7 839 bool ret = false;
237e200e
SH
840 FILE *f;
841 char *line = NULL;
842 size_t len = 0;
843
844 if ((f = fopen("/proc/self/cgroup", "r")) == NULL) {
845 fprintf(stderr, "Error opening /proc/self/cgroup: %s\n", strerror(errno));
846 return false;
847 }
848
849 while (getline(&line, &len, f) != -1) {
850 char *p, *p2;
851
852 p = strchr(line, ':');
853 if (!p)
854 goto out;
855 *(p++) = '\0';
856
857 p2 = strrchr(p, ':');
858 if (!p2)
859 goto out;
860 *p2 = '\0';
861
a67719f6
CB
862 /* With cgroupv2 /proc/self/cgroup can contain entries of the
863 * form: 0::/ This will cause lxcfs to fail the cgroup mounts
864 * because it parses out the empty string "" and later on passes
865 * it to mount(). Let's skip such entries.
866 */
867 if (!strcmp(p, ""))
868 continue;
869
237e200e
SH
870 if (!do_mount_cgroup(p))
871 goto out;
872 }
873 ret = true;
874
875out:
876 free(line);
877 fclose(f);
878 return ret;
879}
880
881static bool cgfs_setup_controllers(void)
882{
883 if (!setup_cgfs_dir()) {
884 return false;
885 }
886
887 if (!do_mount_cgroups()) {
888 fprintf(stderr, "Failed to set up cgroup mounts\n");
889 return false;
890 }
891
892 return true;
893}
894
e190ee91
SH
895static int set_pidfile(char *pidfile)
896{
897 int fd;
898 char buf[50];
899 struct flock fl;
900
901 fl.l_type = F_WRLCK;
902 fl.l_whence = SEEK_SET;
903 fl.l_start = 0;
904 fl.l_len = 0;
905
906 fd = open(pidfile, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
907 if (fd == -1) {
908 fprintf(stderr, "Could not open pidfile %s: %m", pidfile);
909 return -1;
910 }
911
912 if (fcntl(fd, F_SETLK, &fl) == -1) {
913 if (errno == EAGAIN || errno == EACCES) {
914 fprintf(stderr, "PID file '%s' is already locked.\n", pidfile);
915 close(fd);
916 return -1;
917 }
918 fprintf(stderr, "Warning; unable to lock PID file, proceeding.\n");
919 }
920
921 if (ftruncate(fd, 0) == -1) {
922 fprintf(stderr, "Error truncating PID file '%s': %m", pidfile);
923 close(fd);
924 return -1;
925 }
926
927 snprintf(buf, 50, "%ld\n", (long) getpid());
928 if (write(fd, buf, strlen(buf)) != strlen(buf)) {
929 fprintf(stderr, "Error writing to PID file '%s': %m", pidfile);
930 close(fd);
931 return -1;
932 }
933
934 return fd;
935}
936
758ad80c
SH
937int main(int argc, char *argv[])
938{
e190ee91
SH
939 int ret = -1, pidfd;
940 char *pidfile = NULL, *v = NULL;
941 size_t pidfile_len;
0b0f73db
SH
942 /*
943 * what we pass to fuse_main is:
944 * argv[0] -s -f -o allow_other,directio argv[1] NULL
945 */
2c51f8dd
SH
946 int nargs = 5, cnt = 0;
947 char *newargv[6];
758ad80c 948
0b0f73db
SH
949 /* accomodate older init scripts */
950 swallow_arg(&argc, argv, "-s");
951 swallow_arg(&argc, argv, "-f");
e190ee91
SH
952 if (swallow_option(&argc, argv, "-o", &v)) {
953 if (strcmp(v, "allow_other") != 0) {
954 fprintf(stderr, "Warning: unexpected fuse option %s\n", v);
955 exit(1);
956 }
957 free(v);
958 v = NULL;
959 }
960 if (swallow_option(&argc, argv, "-p", &v))
961 pidfile = v;
0b0f73db 962
2e9c0b32
SH
963 if (argc == 2 && strcmp(argv[1], "--version") == 0) {
964 fprintf(stderr, "%s\n", VERSION);
965 exit(0);
966 }
0b0f73db 967 if (argc != 2 || is_help(argv[1]))
758ad80c
SH
968 usage(argv[0]);
969
59120f04 970 do_reload();
8fad16cd 971 if (signal(SIGUSR1, reload_handler) == SIG_ERR) {
8dcde249 972 fprintf(stderr, "Error setting USR1 signal handler: %m\n");
8fad16cd
SH
973 exit(1);
974 }
e190ee91 975
38a76a91 976 newargv[cnt++] = argv[0];
38a76a91
SH
977 newargv[cnt++] = "-f";
978 newargv[cnt++] = "-o";
f466a31e 979 newargv[cnt++] = "allow_other,direct_io,entry_timeout=0.5,attr_timeout=0.5";
38a76a91
SH
980 newargv[cnt++] = argv[1];
981 newargv[cnt++] = NULL;
758ad80c 982
35482f91 983 if (!cgfs_setup_controllers())
c0adec85 984 goto out;
758ad80c 985
e190ee91
SH
986 if (!pidfile) {
987 pidfile_len = strlen(RUNTIME_PATH) + strlen("/lxcfs.pid") + 1;
988 pidfile = alloca(pidfile_len);
989 snprintf(pidfile, pidfile_len, "%s/lxcfs.pid", RUNTIME_PATH);
990 }
991 if ((pidfd = set_pidfile(pidfile)) < 0)
992 goto out;
993
35482f91 994 ret = fuse_main(nargs, newargv, &lxcfs_ops, NULL);
758ad80c 995
237e200e 996 dlclose(dlopen_handle);
e190ee91
SH
997 unlink(pidfile);
998 close(pidfd);
237e200e 999
c0adec85 1000out:
758ad80c 1001 return ret;
2183082c 1002}