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