1 /*-*- Mode: C; c-basic-offset: 8 -*-*/
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
28 #include <libcgroup.h>
30 #include "cgroup-util.h"
36 int cg_translate_error(int error
, int _errno
) {
40 case ECGROUPNOTCOMPILED
:
41 case ECGROUPNOTMOUNTED
:
43 case ECGROUPNOTCREATED
:
49 case ECGROUPNOTALLOWED
:
59 static struct cgroup
* cg_new(const char *controller
, const char *path
) {
60 struct cgroup
*cgroup
;
65 if (!(cgroup
= cgroup_new_cgroup(path
)))
68 if (!cgroup_add_controller(cgroup
, controller
)) {
76 int cg_kill(const char *controller
, const char *path
, int sig
, bool ignore_self
) {
77 bool killed
= false, done
= false;
86 /* This goes through the tasks list and kills them all. This
87 * is repeated until no further processes are added to the
88 * tasks list, to properly handle forking processes */
90 if (!(s
= set_new(trivial_hash_func
, trivial_compare_func
)))
96 void *iterator
= NULL
;
101 r
= cgroup_get_task_begin(path
, controller
, &iterator
, &pid
);
104 if (pid
== my_pid
&& ignore_self
)
107 if (set_get(s
, INT_TO_PTR(pid
)) == INT_TO_PTR(pid
))
110 /* If we haven't killed this process yet, kill
113 if (kill(pid
, sig
) < 0 && errno
!= ESRCH
) {
121 if ((r
= set_put(s
, INT_TO_PTR(pid
))) < 0)
125 r
= cgroup_get_task_next(&iterator
, &pid
);
128 if (r
== 0 || r
== ECGEOF
)
130 else if (r
== ECGOTHER
&& errno
== ENOENT
)
133 r
= cg_translate_error(r
, errno
);
136 assert_se(cgroup_get_task_end(&iterator
) == 0);
138 /* To avoid racing against processes which fork
139 * quicker than we can kill them we repeat this until
140 * no new pids need to be killed. */
142 } while (!done
&& r
>= 0);
155 int cg_kill_recursive(const char *controller
, const char *path
, int sig
, bool ignore_self
) {
156 struct cgroup_file_info info
;
157 int level
= 0, r
, ret
= 0;
158 void *iterator
= NULL
;
167 r
= cgroup_walk_tree_begin(controller
, path
, 0, &iterator
, &info
, &level
);
172 if (info
.type
!= CGROUP_FILE_TYPE_DIR
)
175 if (asprintf(&p
, "%s/%s", path
, info
.path
) < 0) {
180 k
= cg_kill(controller
, p
, sig
, ignore_self
);
191 r
= cgroup_walk_tree_next(0, &iterator
, &info
, level
);
195 if (r
== 0 || r
== ECGEOF
)
197 else if (r
== ECGOTHER
&& errno
== ENOENT
)
200 ret
= cg_translate_error(r
, errno
);
203 assert_se(cgroup_walk_tree_end(&iterator
) == 0);
208 int cg_kill_recursive_and_wait(const char *controller
, const char *path
) {
214 /* This safely kills all processes; first it sends a SIGTERM,
215 * then checks 8 times after 50ms whether the group is
216 * now empty, and finally kills everything that is left with
219 for (i
= 0; i
< 10; i
++) {
229 if ((r
= cg_kill_recursive(controller
, path
, sig
, true)) <= 0)
232 usleep(50 * USEC_PER_MSEC
);
238 int cg_migrate(const char *controller
, const char *from
, const char *to
, bool ignore_self
) {
239 bool migrated
= false, done
= false;
248 if (!(dest
= cg_new(controller
, to
)))
254 void *iterator
= NULL
;
259 r
= cgroup_get_task_begin(from
, controller
, &iterator
, &pid
);
262 if (pid
== my_pid
&& ignore_self
)
265 if ((r
= cgroup_attach_task_pid(dest
, pid
)) != 0) {
267 r
= cg_translate_error(r
, errno
);
275 r
= cgroup_get_task_next(&iterator
, &pid
);
278 if (r
== 0 || r
== ECGEOF
)
280 else if (r
== ECGOTHER
&& errno
== ENOENT
)
283 r
= cg_translate_error(r
, errno
);
285 assert_se(cgroup_get_task_end(&iterator
) == 0);
287 } while (!done
&& r
>= 0);
300 int cg_migrate_recursive(const char *controller
, const char *from
, const char *to
, bool ignore_self
) {
301 struct cgroup_file_info info
;
302 int level
= 0, r
, ret
= 0;
303 void *iterator
= NULL
;
304 bool migrated
= false;
312 r
= cgroup_walk_tree_begin(controller
, from
, 0, &iterator
, &info
, &level
);
317 if (info
.type
!= CGROUP_FILE_TYPE_DIR
)
320 if (asprintf(&p
, "%s/%s", from
, info
.path
) < 0) {
325 k
= cg_migrate(controller
, p
, to
, ignore_self
);
335 r
= cgroup_walk_tree_next(0, &iterator
, &info
, level
);
339 if (r
== 0 || r
== ECGEOF
)
341 else if (r
== ECGOTHER
&& errno
== ENOENT
)
344 r
= cg_translate_error(r
, errno
);
347 assert_se(cgroup_walk_tree_end(&iterator
) == 0);
352 int cg_get_path(const char *controller
, const char *path
, const char *suffix
, char **fs
) {
359 if ((r
= cgroup_get_subsys_mount_point(controller
, &mp
)) != 0)
360 return cg_translate_error(r
, errno
);
363 r
= asprintf(fs
, "%s/%s/%s", mp
, path
, suffix
);
365 r
= asprintf(fs
, "%s/%s", mp
, path
);
369 return r
< 0 ? -ENOMEM
: 0;
372 int cg_trim(const char *controller
, const char *path
, bool delete_root
) {
379 if ((r
= cg_get_path(controller
, path
, NULL
, &fs
)) < 0)
382 r
= rm_rf(fs
, true, delete_root
);
388 int cg_delete(const char *controller
, const char *path
) {
395 if (!(cg
= cg_new(controller
, path
)))
398 if ((r
= cgroup_delete_cgroup_ext(cg
, CGFLAG_DELETE_RECURSIVE
|CGFLAG_DELETE_IGNORE_MIGRATION
)) != 0) {
399 r
= cg_translate_error(r
, errno
);
411 int cg_create(const char *controller
, const char *path
) {
418 if (!(cg
= cg_new(controller
, path
)))
421 if ((r
= cgroup_create_cgroup(cg
, 1)) != 0) {
422 r
= cg_translate_error(r
, errno
);
434 int cg_attach(const char *controller
, const char *path
, pid_t pid
) {
442 if (!(cg
= cg_new(controller
, path
)))
448 if ((r
= cgroup_attach_task_pid(cg
, pid
))) {
449 r
= cg_translate_error(r
, errno
);
461 int cg_create_and_attach(const char *controller
, const char *path
, pid_t pid
) {
469 if (!(cg
= cg_new(controller
, path
)))
472 if ((r
= cgroup_create_cgroup(cg
, 1)) != 0) {
473 r
= cg_translate_error(r
, errno
);
480 if ((r
= cgroup_attach_task_pid(cg
, pid
))) {
481 r
= cg_translate_error(r
, errno
);
493 int cg_set_group_access(const char *controller
, const char *path
, mode_t mode
, uid_t uid
, gid_t gid
) {
500 if ((r
= cg_get_path(controller
, path
, NULL
, &fs
)) < 0)
503 r
= chmod_and_chown(fs
, mode
, uid
, gid
);
509 int cg_set_task_access(const char *controller
, const char *path
, mode_t mode
, uid_t uid
, gid_t gid
) {
516 if ((r
= cg_get_path(controller
, path
, "tasks", &fs
)) < 0)
519 r
= chmod_and_chown(fs
, mode
, uid
, gid
);
525 int cg_get_by_pid(const char *controller
, pid_t pid
, char **path
) {
533 if ((r
= cgroup_get_current_controller_path(pid
, controller
, &p
)) != 0)
534 return cg_translate_error(r
, errno
);
542 int cg_install_release_agent(const char *controller
, const char *agent
) {
543 char *mp
= NULL
, *path
= NULL
, *contents
= NULL
, *line
= NULL
, *sc
;
549 if ((r
= cgroup_get_subsys_mount_point(controller
, &mp
)) != 0)
550 return cg_translate_error(r
, errno
);
552 if (asprintf(&path
, "%s/release_agent", mp
) < 0) {
557 if ((r
= read_one_line_file(path
, &contents
)) < 0)
560 sc
= strstrip(contents
);
564 if (asprintf(&line
, "%s\n", agent
) < 0) {
569 if ((r
= write_one_line_file(path
, line
)) < 0)
572 } else if (!streq(sc
, agent
)) {
579 if (asprintf(&path
, "%s/notify_on_release", mp
) < 0) {
586 if ((r
= read_one_line_file(path
, &contents
)) < 0)
589 sc
= strstrip(contents
);
591 if (streq(sc
, "0")) {
592 if ((r
= write_one_line_file(path
, "1\n")) < 0)
594 } else if (!streq(sc
, "1")) {
610 int cg_is_empty(const char *controller
, const char *path
, bool ignore_self
) {
611 void *iterator
= NULL
;
618 r
= cgroup_get_task_begin(path
, controller
, &iterator
, &pid
);
621 if (ignore_self
&& pid
== getpid())
627 r
= cgroup_get_task_next(&iterator
, &pid
);
634 r
= cg_translate_error(r
, errno
);
638 assert_se(cgroup_get_task_end(&iterator
) == 0);
643 int cg_is_empty_recursive(const char *controller
, const char *path
, bool ignore_self
) {
644 struct cgroup_file_info info
;
645 int level
= 0, r
, ret
= 0;
646 void *iterator
= NULL
;
654 r
= cgroup_walk_tree_begin(controller
, path
, 0, &iterator
, &info
, &level
);
659 if (info
.type
!= CGROUP_FILE_TYPE_DIR
)
662 if (asprintf(&p
, "%s/%s", path
, info
.path
) < 0) {
667 k
= cg_is_empty(controller
, p
, ignore_self
);
679 r
= cgroup_walk_tree_next(0, &iterator
, &info
, level
);
683 if (r
== 0 || r
== ECGEOF
)
685 else if (r
== ECGOTHER
&& errno
== ENOENT
)
688 ret
= cg_translate_error(r
, errno
);
691 assert_se(cgroup_walk_tree_end(&iterator
) == 0);