]>
git.proxmox.com Git - mirror_lxcfs.git/blob - cgfs.c
2 * Copyright © 2015 Canonical Limited
5 * Serge Hallyn <serge.hallyn@ubuntu.com>
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
33 #include <sys/types.h>
35 #include <sys/param.h>
36 #include <sys/mount.h>
41 /* directory under which we mount the controllers - /run/lxcfs/controllers */
43 #define basedir RUNTIME_PATH "/lxcfs/controllers"
45 int is_dir(const char *path
)
48 int ret
= stat(path
, &statbuf
);
49 if (ret
== 0 && S_ISDIR(statbuf
.st_mode
))
54 char *must_copy_string(const char *str
)
66 static inline void drop_trailing_newlines(char *s
)
70 for (l
=strlen(s
); l
>0 && s
[l
-1] == '\n'; l
--)
75 static void dorealloc(char **mem
, size_t oldlen
, size_t newlen
)
77 int newbatches
= (newlen
/ BATCH_SIZE
) + 1;
78 int oldbatches
= (oldlen
/ BATCH_SIZE
) + 1;
80 if (!*mem
|| newbatches
> oldbatches
) {
83 tmp
= realloc(*mem
, newbatches
* BATCH_SIZE
);
88 static void append_line(char **contents
, size_t *len
, char *line
, ssize_t linelen
)
90 size_t newlen
= *len
+ linelen
;
91 dorealloc(contents
, *len
, newlen
+ 1);
92 memcpy(*contents
+ *len
, line
, linelen
+1);
96 static char *read_file(const char *from
)
99 char *contents
= NULL
;
100 FILE *f
= fopen(from
, "r");
101 size_t len
= 0, fulllen
= 0;
107 while ((linelen
= getline(&line
, &len
, f
)) != -1) {
108 append_line(&contents
, &fulllen
, line
, linelen
);
113 drop_trailing_newlines(contents
);
118 static bool write_string(const char *fnam
, const char *string
)
123 if (!(f
= fopen(fnam
, "w")))
125 len
= strlen(string
);
126 ret
= fwrite(string
, 1, len
, f
);
128 fprintf(stderr
, "Error writing to file: %s\n", strerror(errno
));
133 fprintf(stderr
, "Error writing to file: %s\n", strerror(errno
));
139 static bool store_hierarchy(char *stridx
, char *h
)
141 int idx
= atoi(stridx
);
142 size_t needed_len
= (idx
+ 1) * sizeof(char *);
144 if (idx
< 0 || idx
> 30) {
145 fprintf(stderr
, "Error: corrupt /proc/self/cgroup\n");
150 hierarchies
= malloc(needed_len
);
151 memset(hierarchies
, 0, needed_len
);
152 num_hierarchies
= idx
+ 1;
153 } else if (idx
>= num_hierarchies
) {
155 size_t old_len
= (num_hierarchies
+ 1) * sizeof(char *);
157 tmp
= malloc(needed_len
);
159 memset(tmp
, 0, needed_len
);
160 memcpy(tmp
, hierarchies
, old_len
);
163 num_hierarchies
= idx
+ 1;
166 if (hierarchies
[idx
]) {
167 fprintf(stderr
, "Error: corrupt /proc/self/cgroup\n");
170 hierarchies
[idx
] = must_copy_string(h
);
174 void print_subsystems(void)
178 fprintf(stderr
, "hierarchies:");
179 for (i
= 0; i
< num_hierarchies
; i
++) {
181 fprintf(stderr
, " %d: %s\n", i
, hierarchies
[i
]);
185 static bool collect_subsystems(void)
192 if ((f
= fopen("/proc/self/cgroup", "r")) == NULL
) {
193 fprintf(stderr
, "Error opening /proc/self/cgroup: %s\n", strerror(errno
));
196 while (getline(&line
, &len
, f
) != -1) {
199 p
= strchr(line
, ':');
204 p2
= strrchr(p
, ':');
209 if (!store_hierarchy(line
, p
))
222 static bool do_mount_cgroups(void)
225 char target
[MAXPATHLEN
];
227 for (i
=0; i
<num_hierarchies
; i
++) {
230 ret
= snprintf(target
, MAXPATHLEN
, "%s/%s", basedir
, hierarchies
[i
]);
231 if (ret
< 0 || ret
>= MAXPATHLEN
)
233 if (mkdir(target
, 0755) < 0 && errno
!= EEXIST
)
235 if (mount(hierarchies
[i
], target
, "cgroup", 0, hierarchies
[i
]) < 0) {
236 fprintf(stderr
, "Failed mounting cgroups\n");
244 static bool umount_if_mounted(void)
246 if (umount2(basedir
, MNT_DETACH
) < 0 && errno
!= EINVAL
) {
247 fprintf(stderr
, "failed to umount %s: %s\n", basedir
,
254 static bool mkdir_p(const char *dir
, mode_t mode
)
256 const char *tmp
= dir
;
257 const char *orig
= dir
;
261 dir
= tmp
+ strspn(tmp
, "/");
262 tmp
= dir
+ strcspn(dir
, "/");
263 makeme
= strndup(orig
, dir
- orig
);
265 if (mkdir(makeme
, mode
) && errno
!= EEXIST
) {
266 fprintf(stderr
, "failed to create directory '%s': %s",
267 makeme
, strerror(errno
));
277 static bool setup_cgfs_dir(void)
279 if (!mkdir_p(basedir
, 0700)) {
280 fprintf(stderr
, "Failed to create lxcfs cgdir\n");
283 if (!umount_if_mounted()) {
284 fprintf(stderr
, "Failed to clean up old lxcfs cgdir\n");
287 if (mount("tmpfs", basedir
, "tmpfs", 0, "size=100000,mode=700") < 0) {
288 fprintf(stderr
, "Failed to mount tmpfs for private controllers\n");
294 bool cgfs_setup_controllers(void)
296 if (!setup_cgfs_dir()) {
300 if (!collect_subsystems()) {
301 fprintf(stderr
, "failed to collect cgroup subsystems\n");
305 if (!do_mount_cgroups()) {
306 fprintf(stderr
, "Failed to set up cgroup mounts\n");
313 static bool in_comma_list(const char *needle
, const char *haystack
)
315 const char *s
= haystack
, *e
;
316 size_t nlen
= strlen(needle
);
318 while (*s
&& (e
= index(s
, ','))) {
323 if (strncmp(needle
, s
, nlen
) == 0)
327 if (strcmp(needle
, s
) == 0)
332 /* do we need to do any massaging here? I'm not sure... */
333 char *find_mounted_controller(const char *controller
)
337 for (i
= 0; i
< num_hierarchies
; i
++) {
340 if (strcmp(hierarchies
[i
], controller
) == 0)
341 return hierarchies
[i
];
342 if (in_comma_list(controller
, hierarchies
[i
]))
343 return hierarchies
[i
];
349 bool cgfs_set_value(const char *controller
, const char *cgroup
, const char *file
,
353 char *fnam
, *tmpc
= find_mounted_controller(controller
);
357 /* basedir / tmpc / cgroup / file \0 */
358 len
= strlen(basedir
) + strlen(tmpc
) + strlen(cgroup
) + strlen(file
) + 4;
360 snprintf(fnam
, len
, "%s/%s/%s/%s", basedir
, tmpc
, cgroup
, file
);
362 return write_string(fnam
, value
);
365 // Chown all the files in the cgroup directory. We do this when we create
366 // a cgroup on behalf of a user.
367 static void chown_all_cgroup_files(const char *dirname
, uid_t uid
, gid_t gid
)
369 struct dirent dirent
, *direntp
;
370 char path
[MAXPATHLEN
];
375 len
= strlen(dirname
);
376 if (len
>= MAXPATHLEN
) {
377 fprintf(stderr
, "chown_all_cgroup_files: pathname too long: %s\n", dirname
);
381 d
= opendir(dirname
);
383 fprintf(stderr
, "chown_all_cgroup_files: failed to open %s\n", dirname
);
387 while (readdir_r(d
, &dirent
, &direntp
) == 0 && direntp
) {
388 if (!strcmp(direntp
->d_name
, ".") || !strcmp(direntp
->d_name
, ".."))
390 ret
= snprintf(path
, MAXPATHLEN
, "%s/%s", dirname
, direntp
->d_name
);
391 if (ret
< 0 || ret
>= MAXPATHLEN
) {
392 fprintf(stderr
, "chown_all_cgroup_files: pathname too long under %s\n", dirname
);
395 if (chown(path
, uid
, gid
) < 0)
396 fprintf(stderr
, "Failed to chown file %s to %u:%u", path
, uid
, gid
);
401 int cgfs_create(const char *controller
, const char *cg
, uid_t uid
, gid_t gid
)
404 char *dirnam
, *tmpc
= find_mounted_controller(controller
);
408 /* basedir / tmpc / cg \0 */
409 len
= strlen(basedir
) + strlen(tmpc
) + strlen(cg
) + 3;
410 dirnam
= alloca(len
);
411 snprintf(dirnam
, len
, "%s/%s/%s", basedir
,tmpc
, cg
);
413 if (mkdir(dirnam
, 0755) < 0)
416 if (uid
== 0 && gid
== 0)
419 if (chown(dirnam
, uid
, gid
) < 0)
422 chown_all_cgroup_files(dirnam
, uid
, gid
);
427 static bool recursive_rmdir(const char *dirname
)
429 struct dirent dirent
, *direntp
;
432 char pathname
[MAXPATHLEN
];
434 dir
= opendir(dirname
);
436 fprintf(stderr
, "%s: failed to open %s: %s\n", __func__
, dirname
, strerror(errno
));
440 while (!readdir_r(dir
, &dirent
, &direntp
)) {
447 if (!strcmp(direntp
->d_name
, ".") ||
448 !strcmp(direntp
->d_name
, ".."))
451 rc
= snprintf(pathname
, MAXPATHLEN
, "%s/%s", dirname
, direntp
->d_name
);
452 if (rc
< 0 || rc
>= MAXPATHLEN
) {
453 fprintf(stderr
, "pathname too long\n");
457 ret
= lstat(pathname
, &mystat
);
459 fprintf(stderr
, "%s: failed to stat %s: %s\n", __func__
, pathname
, strerror(errno
));
462 if (S_ISDIR(mystat
.st_mode
)) {
463 if (!recursive_rmdir(pathname
))
464 fprintf(stderr
, "Error removing %s\n", pathname
);
469 if (closedir(dir
) < 0) {
470 fprintf(stderr
, "%s: failed to close directory %s: %s\n", __func__
, dirname
, strerror(errno
));
474 if (rmdir(dirname
) < 0) {
475 fprintf(stderr
, "%s: failed to delete %s: %s\n", __func__
, dirname
, strerror(errno
));
482 bool cgfs_remove(const char *controller
, const char *cg
)
485 char *dirnam
, *tmpc
= find_mounted_controller(controller
);
489 /* basedir / tmpc / cg \0 */
490 len
= strlen(basedir
) + strlen(tmpc
) + strlen(cg
) + 3;
491 dirnam
= alloca(len
);
492 snprintf(dirnam
, len
, "%s/%s/%s", basedir
,tmpc
, cg
);
493 return recursive_rmdir(dirnam
);
496 bool cgfs_chmod_file(const char *controller
, const char *file
, mode_t mode
)
499 char *pathname
, *tmpc
= find_mounted_controller(controller
);
503 /* basedir / tmpc / file \0 */
504 len
= strlen(basedir
) + strlen(tmpc
) + strlen(file
) + 3;
505 pathname
= alloca(len
);
506 snprintf(pathname
, len
, "%s/%s/%s", basedir
, tmpc
, file
);
507 if (chmod(pathname
, mode
) < 0)
512 static int chown_tasks_files(const char *dirname
, uid_t uid
, gid_t gid
)
517 len
= strlen(dirname
) + strlen("/cgroup.procs") + 1;
519 snprintf(fname
, len
, "%s/tasks", dirname
);
520 if (chown(fname
, uid
, gid
) != 0)
522 snprintf(fname
, len
, "%s/cgroup.procs", dirname
);
523 if (chown(fname
, uid
, gid
) != 0)
528 int cgfs_chown_file(const char *controller
, const char *file
, uid_t uid
, gid_t gid
)
531 char *pathname
, *tmpc
= find_mounted_controller(controller
);
535 /* basedir / tmpc / file \0 */
536 len
= strlen(basedir
) + strlen(tmpc
) + strlen(file
) + 3;
537 pathname
= alloca(len
);
538 snprintf(pathname
, len
, "%s/%s/%s", basedir
, tmpc
, file
);
539 if (chown(pathname
, uid
, gid
) < 0)
542 if (is_dir(pathname
))
543 // like cgmanager did, we want to chown the tasks file as well
544 return chown_tasks_files(pathname
, uid
, gid
);
549 FILE *open_pids_file(const char *controller
, const char *cgroup
)
552 char *pathname
, *tmpc
= find_mounted_controller(controller
);
556 /* basedir / tmpc / cgroup / "cgroup.procs" \0 */
557 len
= strlen(basedir
) + strlen(tmpc
) + strlen(cgroup
) + 4 + strlen("cgroup.procs");
558 pathname
= alloca(len
);
559 snprintf(pathname
, len
, "%s/%s/%s/cgroup.procs", basedir
, tmpc
, cgroup
);
560 return fopen(pathname
, "w");
563 bool cgfs_list_children(const char *controller
, const char *cgroup
, char ***list
)
566 char *dirname
, *tmpc
= find_mounted_controller(controller
);
567 char pathname
[MAXPATHLEN
];
568 size_t sz
= 0, asz
= BATCH_SIZE
;
569 struct dirent dirent
, *direntp
;
574 *list
= malloc(asz
* sizeof(char *));
581 /* basedir / tmpc / cgroup \0 */
582 len
= strlen(basedir
) + strlen(tmpc
) + strlen(cgroup
) + 3;
583 dirname
= alloca(len
);
584 snprintf(dirname
, len
, "%s/%s/%s", basedir
, tmpc
, cgroup
);
586 dir
= opendir(dirname
);
590 while (!readdir_r(dir
, &dirent
, &direntp
)) {
597 if (!strcmp(direntp
->d_name
, ".") ||
598 !strcmp(direntp
->d_name
, ".."))
601 rc
= snprintf(pathname
, MAXPATHLEN
, "%s/%s", dirname
, direntp
->d_name
);
602 if (rc
< 0 || rc
>= MAXPATHLEN
) {
603 fprintf(stderr
, "%s: pathname too long under %s\n", __func__
, dirname
);
607 ret
= lstat(pathname
, &mystat
);
609 fprintf(stderr
, "%s: failed to stat %s: %s\n", __func__
, pathname
, strerror(errno
));
612 if (!S_ISDIR(mystat
.st_mode
))
619 tmp
= realloc(*list
, asz
* sizeof(char *));
624 (*list
)[sz
] = strdup(direntp
->d_name
);
625 } while (!(*list
)[sz
]);
626 (*list
)[sz
+1] = NULL
;
629 if (closedir(dir
) < 0) {
630 fprintf(stderr
, "%s: failed closedir for %s: %s\n", __func__
, dirname
, strerror(errno
));
636 void free_key(struct cgfs_files
*k
)
644 void free_keys(struct cgfs_files
**keys
)
650 for (i
= 0; keys
[i
]; i
++) {
656 bool cgfs_get_value(const char *controller
, const char *cgroup
, const char *file
, char **value
)
659 char *fnam
, *tmpc
= find_mounted_controller(controller
);
663 /* basedir / tmpc / cgroup / file \0 */
664 len
= strlen(basedir
) + strlen(tmpc
) + strlen(cgroup
) + strlen(file
) + 4;
666 snprintf(fnam
, len
, "%s/%s/%s/%s", basedir
, tmpc
, cgroup
, file
);
668 *value
= read_file(fnam
);
669 return *value
!= NULL
;
672 struct cgfs_files
*cgfs_get_key(const char *controller
, const char *cgroup
, const char *file
)
675 char *fnam
, *tmpc
= find_mounted_controller(controller
);
677 struct cgfs_files
*newkey
;
683 if (file
&& *file
== '/')
686 if (file
&& index(file
, '/'))
689 /* basedir / tmpc / cgroup / file \0 */
690 len
= strlen(basedir
) + strlen(tmpc
) + strlen(cgroup
) + 3;
692 len
+= strlen(file
) + 1;
694 snprintf(fnam
, len
, "%s/%s/%s/%s", basedir
, tmpc
, cgroup
,
695 file
? "/" : "", file
? file
: "");
697 ret
= stat(fnam
, &sb
);
701 if (!S_ISREG(sb
.st_mode
))
704 newkey
= malloc(sizeof(struct cgfs_files
));
706 newkey
->name
= must_copy_string(file
);
707 newkey
->uid
= sb
.st_uid
;
708 newkey
->gid
= sb
.st_gid
;
709 newkey
->mode
= sb
.st_mode
;
714 bool cgfs_list_keys(const char *controller
, const char *cgroup
, struct cgfs_files
***keys
)
717 char *dirname
, *tmpc
= find_mounted_controller(controller
);
718 char pathname
[MAXPATHLEN
];
719 size_t sz
= 0, asz
= 0;
720 struct dirent dirent
, *direntp
;
728 /* basedir / tmpc / cgroup \0 */
729 len
= strlen(basedir
) + strlen(tmpc
) + strlen(cgroup
) + 3;
730 dirname
= alloca(len
);
731 snprintf(dirname
, len
, "%s/%s/%s", basedir
, tmpc
, cgroup
);
733 dir
= opendir(dirname
);
737 while (!readdir_r(dir
, &dirent
, &direntp
)) {
744 if (!strcmp(direntp
->d_name
, ".") ||
745 !strcmp(direntp
->d_name
, ".."))
748 rc
= snprintf(pathname
, MAXPATHLEN
, "%s/%s", dirname
, direntp
->d_name
);
749 if (rc
< 0 || rc
>= MAXPATHLEN
) {
750 fprintf(stderr
, "%s: pathname too long under %s\n", __func__
, dirname
);
754 ret
= lstat(pathname
, &mystat
);
756 fprintf(stderr
, "%s: failed to stat %s: %s\n", __func__
, pathname
, strerror(errno
));
759 if (!S_ISREG(mystat
.st_mode
))
763 struct cgfs_files
**tmp
;
766 tmp
= realloc(*keys
, asz
* sizeof(struct cgfs_files
*));
770 (*keys
)[sz
] = cgfs_get_key(controller
, cgroup
, direntp
->d_name
);
771 (*keys
)[sz
+1] = NULL
;
773 fprintf(stderr
, "%s: Error getting files under %s:%s\n",
774 __func__
, controller
, cgroup
);
779 if (closedir(dir
) < 0) {
780 fprintf(stderr
, "%s: failed closedir for %s: %s\n", __func__
, dirname
, strerror(errno
));
786 bool is_child_cgroup(const char *controller
, const char *cgroup
, const char *f
)
788 char *fnam
, *tmpc
= find_mounted_controller(controller
);
794 /* basedir / tmpc / cgroup / f \0 */
795 len
= strlen(basedir
) + strlen(tmpc
) + strlen(cgroup
) + strlen(f
) + 4;
797 snprintf(fnam
, len
, "%s/%s/%s/%s", basedir
, tmpc
, cgroup
, f
);
799 ret
= stat(fnam
, &sb
);
800 if (ret
< 0 || !S_ISDIR(sb
.st_mode
))