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);