#include <dirent.h>
#include <sys/stat.h>
#include <sys/types.h>
+#include <ftw.h>
#include "cgroup-util.h"
#include "log.h"
return 0;
}
-int cg_rmdir(const char *controller, const char *path) {
+int cg_rmdir(const char *controller, const char *path, bool honour_sticky) {
char *p;
int r;
- if ((r = cg_get_path(controller, path, NULL, &p)) < 0)
+ r = cg_get_path(controller, path, NULL, &p);
+ if (r < 0)
return r;
+ if (honour_sticky) {
+ char *tasks;
+
+ /* If the sticky bit is set don't remove the directory */
+
+ tasks = strappend(p, "/tasks");
+ if (!tasks) {
+ free(p);
+ return -ENOMEM;
+ }
+
+ r = file_is_sticky(tasks);
+ free(tasks);
+
+ if (r > 0) {
+ free(p);
+ return 0;
+ }
+ }
+
r = rmdir(p);
free(p);
- return r < 0 ? -errno : 0;
+ return (r < 0 && errno != ENOENT) ? -errno : 0;
}
-int cg_kill(const char *controller, const char *path, int sig, bool ignore_self, Set *s) {
+int cg_kill(const char *controller, const char *path, int sig, bool sigcont, bool ignore_self, Set *s) {
bool done = false;
int r, ret = 0;
pid_t my_pid;
my_pid = getpid();
do {
- pid_t pid;
+ pid_t pid = 0;
done = true;
if ((r = cg_enumerate_processes(controller, path, &f)) < 0) {
if (kill(pid, sig) < 0) {
if (ret >= 0 && errno != ESRCH)
ret = -errno;
- } else if (ret == 0)
+ } else if (ret == 0) {
+
+ if (sigcont)
+ kill(pid, SIGCONT);
+
ret = 1;
+ }
done = false;
return ret;
}
-int cg_kill_recursive(const char *controller, const char *path, int sig, bool ignore_self, bool rem, Set *s) {
+int cg_kill_recursive(const char *controller, const char *path, int sig, bool sigcont, bool ignore_self, bool rem, Set *s) {
int r, ret = 0;
DIR *d = NULL;
char *fn;
if (!(s = allocated_set = set_new(trivial_hash_func, trivial_compare_func)))
return -ENOMEM;
- ret = cg_kill(controller, path, sig, ignore_self, s);
+ ret = cg_kill(controller, path, sig, sigcont, ignore_self, s);
if ((r = cg_enumerate_subgroups(controller, path, &d)) < 0) {
if (ret >= 0 && r != -ENOENT)
goto finish;
}
- r = cg_kill_recursive(controller, p, sig, ignore_self, rem, s);
+ r = cg_kill_recursive(controller, p, sig, sigcont, ignore_self, rem, s);
free(p);
if (r != 0 && ret >= 0)
ret = r;
if (rem)
- if ((r = cg_rmdir(controller, path)) < 0) {
+ if ((r = cg_rmdir(controller, path, true)) < 0) {
if (ret >= 0 &&
r != -ENOENT &&
r != -EBUSY)
else
sig = 0;
- if ((r = cg_kill_recursive(controller, path, sig, true, rem, NULL)) <= 0)
+ if ((r = cg_kill_recursive(controller, path, sig, true, true, rem, NULL)) <= 0)
return r;
usleep(200 * USEC_PER_MSEC);
my_pid = getpid();
do {
- pid_t pid;
+ pid_t pid = 0;
done = true;
if ((r = cg_enumerate_tasks(controller, from, &f)) < 0) {
ret = r;
if (rem)
- if ((r = cg_rmdir(controller, from)) < 0) {
+ if ((r = cg_rmdir(controller, from, true)) < 0) {
if (ret >= 0 &&
r != -ENOENT &&
r != -EBUSY)
int cg_get_path(const char *controller, const char *path, const char *suffix, char **fs) {
const char *p;
- char *mp;
- int r;
+ char *t;
+ static __thread bool good = false;
assert(controller);
assert(fs);
+ 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;
+ }
+
+ if (isempty(controller))
+ return -EINVAL;
+
/* This is a very minimal lookup from controller names to
* paths. Since we have mounted most hierarchies ourselves
* should be kinda safe, but eventually we might want to
else
p = controller;
- if (asprintf(&mp, "/sys/fs/cgroup/%s", p) < 0)
- return -ENOMEM;
-
- if ((r = path_is_mount_point(mp)) <= 0) {
- free(mp);
- return r < 0 ? r : -ENOENT;
- }
-
if (path && suffix)
- r = asprintf(fs, "%s/%s/%s", mp, path, suffix);
+ t = join("/sys/fs/cgroup/", p, "/", path, "/", suffix, NULL);
else if (path)
- r = asprintf(fs, "%s/%s", mp, path);
+ t = join("/sys/fs/cgroup/", p, "/", path, NULL);
else if (suffix)
- r = asprintf(fs, "%s/%s", mp, suffix);
- else {
- path_kill_slashes(mp);
- *fs = mp;
+ t = join("/sys/fs/cgroup/", p, "/", suffix, NULL);
+ else
+ t = join("/sys/fs/cgroup/", p, NULL);
+
+ if (!t)
+ return -ENOMEM;
+
+ path_kill_slashes(t);
+
+ *fs = t;
+ return 0;
+}
+
+static int trim_cb(const char *path, const struct stat *sb, int typeflag, struct FTW *ftwbuf) {
+ char *p;
+ bool is_sticky;
+
+ if (typeflag != FTW_DP)
+ return 0;
+
+ if (ftwbuf->level < 1)
return 0;
+
+ p = strappend(path, "/tasks");
+ if (!p) {
+ errno = ENOMEM;
+ return 1;
}
- free(mp);
- path_kill_slashes(*fs);
- return r < 0 ? -ENOMEM : 0;
+ is_sticky = file_is_sticky(p) > 0;
+ free(p);
+
+ if (is_sticky)
+ return 0;
+
+ rmdir(path);
+ return 0;
}
int cg_trim(const char *controller, const char *path, bool delete_root) {
char *fs;
- int r;
+ int r = 0;
assert(controller);
assert(path);
- if ((r = cg_get_path(controller, path, NULL, &fs)) < 0)
+ r = cg_get_path(controller, path, NULL, &fs);
+ if (r < 0)
return r;
- r = rm_rf(fs, true, delete_root);
+ errno = 0;
+ if (nftw(fs, trim_cb, 64, FTW_DEPTH|FTW_MOUNT|FTW_PHYS) < 0)
+ r = errno ? -errno : -EIO;
+
+ if (delete_root) {
+ bool is_sticky;
+ char *p;
+
+ p = strappend(fs, "/tasks");
+ if (!p) {
+ free(fs);
+ return -ENOMEM;
+ }
+
+ is_sticky = file_is_sticky(p) > 0;
+ free(p);
+
+ if (!is_sticky)
+ if (rmdir(fs) < 0 && errno != ENOENT) {
+ if (r == 0)
+ r = -errno;
+ }
+ }
+
free(fs);
- return r == -ENOENT ? 0 : r;
+ return r;
}
int cg_delete(const char *controller, const char *path) {
}
int cg_is_empty(const char *controller, const char *path, bool ignore_self) {
- pid_t pid;
+ pid_t pid = 0;
int r;
- FILE *f;
+ FILE *f = NULL;
bool found = false;
assert(controller);
return r;
}
+
+int cg_get_user_path(char **path) {
+ char *root, *p;
+
+ assert(path);
+
+ /* Figure out the place to put user cgroups below. We use the
+ * same as PID 1 has but with the "/system" suffix replaced by
+ * "/user" */
+
+ if (cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, 1, &root) < 0)
+ p = strdup("/user");
+ else {
+ if (endswith(root, "/system"))
+ root[strlen(root) - 7] = 0;
+ else if (streq(root, "/"))
+ root[0] = 0;
+
+ p = strappend(root, "/user");
+ free(root);
+ }
+
+ if (!p)
+ return -ENOMEM;
+
+ *path = p;
+ return 0;
+}