/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ /*** This file is part of systemd. Copyright 2010 Lennart Poettering systemd is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. systemd is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with systemd; If not, see . ***/ #include #include #include #include #include #include "cgroup-util.h" #include "set.h" #include "path-util.h" int cg_enumerate_processes(const char *controller, const char *path, FILE **_f) { _cleanup_free_ char *fs = NULL; FILE *f; int r; assert(_f); r = cg_get_path(controller, path, "cgroup.procs", &fs); if (r < 0) return r; f = fopen(fs, "re"); if (!f) return -errno; *_f = f; return 0; } int cg_read_pid(FILE *f, pid_t *_pid) { unsigned long ul; /* Note that the cgroup.procs might contain duplicates! See * cgroups.txt for details. */ assert(f); assert(_pid); errno = 0; if (fscanf(f, "%lu", &ul) != 1) { if (feof(f)) return 0; return errno ? -errno : -EIO; } if (ul <= 0) return -EIO; *_pid = (pid_t) ul; return 1; } int cg_kill(const char *controller, const char *path, int sig, bool sigcont, bool ignore_self, Set *s) { _cleanup_set_free_ Set *allocated_set = NULL; bool done = false; int r, ret = 0; pid_t my_pid; assert(sig >= 0); /* This goes through the tasks list and kills them all. This * is repeated until no further processes are added to the * tasks list, to properly handle forking processes */ if (!s) { s = allocated_set = set_new(trivial_hash_func, trivial_compare_func); if (!s) return -ENOMEM; } my_pid = getpid(); do { _cleanup_fclose_ FILE *f = NULL; pid_t pid = 0; done = true; r = cg_enumerate_processes(controller, path, &f); if (r < 0) { if (ret >= 0 && r != -ENOENT) return r; return ret; } while ((r = cg_read_pid(f, &pid)) > 0) { if (ignore_self && pid == my_pid) continue; if (set_get(s, LONG_TO_PTR(pid)) == LONG_TO_PTR(pid)) continue; /* If we haven't killed this process yet, kill * it */ if (kill(pid, sig) < 0) { if (ret >= 0 && errno != ESRCH) ret = -errno; } else { if (sigcont) kill(pid, SIGCONT); if (ret == 0) ret = 1; } done = false; r = set_put(s, LONG_TO_PTR(pid)); if (r < 0) { if (ret >= 0) return r; return ret; } } if (r < 0) { if (ret >= 0) return r; return ret; } /* To avoid racing against processes which fork * quicker than we can kill them we repeat this until * no new pids need to be killed. */ } while (!done); return ret; } static const char *normalize_controller(const char *controller) { assert(controller); if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) return "systemd"; else if (startswith(controller, "name=")) return controller + 5; else return controller; } static int join_path(const char *controller, const char *path, const char *suffix, char **fs) { char *t = NULL; if (!isempty(controller)) { if (!isempty(path) && !isempty(suffix)) t = strjoin("/sys/fs/cgroup/", controller, "/", path, "/", suffix, NULL); else if (!isempty(path)) t = strjoin("/sys/fs/cgroup/", controller, "/", path, NULL); else if (!isempty(suffix)) t = strjoin("/sys/fs/cgroup/", controller, "/", suffix, NULL); else t = strappend("/sys/fs/cgroup/", controller); } else { if (!isempty(path) && !isempty(suffix)) t = strjoin(path, "/", suffix, NULL); else if (!isempty(path)) t = strdup(path); else return -EINVAL; } if (!t) return -ENOMEM; *fs = path_kill_slashes(t); return 0; } int cg_get_path(const char *controller, const char *path, const char *suffix, char **fs) { const char *p; static thread_local bool good = false; assert(fs); if (controller && !cg_controller_is_valid(controller, true)) return -EINVAL; if (_unlikely_(!good)) { int r; r = path_is_mount_point("/sys/fs/cgroup", false); if (r <= 0) return r < 0 ? r : -ENOENT; /* Cache this to save a few stat()s */ good = true; } p = controller ? normalize_controller(controller) : NULL; return join_path(p, path, suffix, fs); } #define CONTROLLER_VALID \ DIGITS LETTERS \ "_" bool cg_controller_is_valid(const char *p, bool allow_named) { const char *t, *s; if (!p) return false; if (allow_named) { s = startswith(p, "name="); if (s) p = s; } if (*p == 0 || *p == '_') return false; for (t = p; *t; t++) if (!strchr(CONTROLLER_VALID, *t)) return false; if (t - p > FILENAME_MAX) return false; return true; }