Commit c6c18be3 authored by Lennart Poettering's avatar Lennart Poettering
Browse files

cgroup: kill processes, not tasks and other cgroup changes

parent 55096547
......@@ -483,15 +483,18 @@ systemctl_SOURCES = \
src/systemctl.c \
src/utmp-wtmp.c \
src/dbus-common.c \
src/cgroup-show.c
src/cgroup-show.c \
src/cgroup-util.c
systemctl_CFLAGS = \
$(AM_CFLAGS) \
$(DBUS_CFLAGS)
$(DBUS_CFLAGS) \
$(CGROUP_CLAGS)
systemctl_LDADD = \
libsystemd-basic.la \
$(DBUS_LIBS)
$(DBUS_LIBS) \
$(CGROUP_LIBS)
systemd_notify_SOURCES = \
src/notify.c \
......@@ -516,13 +519,16 @@ systemd_install_CFLAGS = \
systemd_cgls_SOURCES = \
src/systemd-cgls.c \
src/cgroup-show.c
systemd_cgls_LDADD = \
libsystemd-basic.la
src/cgroup-show.c \
src/cgroup-util.c
systemd_cgls_CFLAGS = \
$(AM_CFLAGS)
$(AM_CFLAGS) \
$(CGROUP_CLAGS)
systemd_cgls_LDADD = \
libsystemd-basic.la \
$(CGROUP_LIBS)
systemadm_SOURCES = \
src/systemadm.vala \
......
......@@ -26,6 +26,7 @@
#include "util.h"
#include "macro.h"
#include "cgroup-util.h"
#include "cgroup-show.h"
static int compare(const void *a, const void *b) {
......@@ -40,8 +41,11 @@ static int compare(const void *a, const void *b) {
static char *get_cgroup_path(const char *name) {
if (startswith(name, "name=systemd:"))
name += 13;
if (!name)
return strdup("/cgroup/systemd");
if (startswith(name, SYSTEMD_CGROUP_CONTROLLER ":"))
name += sizeof(SYSTEMD_CGROUP_CONTROLLER);
if (path_startswith(name, "/cgroup"))
return strdup(name);
......@@ -60,14 +64,14 @@ static unsigned ilog10(unsigned long ul) {
return n;
}
static int show_cgroup_full(const char *name, const char *prefix, unsigned n_columns, bool more) {
static int show_cgroup_full(const char *path, const char *prefix, unsigned n_columns, bool more) {
char *fn;
FILE *f;
size_t n = 0, n_allocated = 0;
pid_t *pids = NULL;
char *p;
pid_t pid, biggest = 0;
int r;
unsigned long biggest = 0;
if (n_columns <= 0)
n_columns = columns();
......@@ -75,7 +79,7 @@ static int show_cgroup_full(const char *name, const char *prefix, unsigned n_col
if (!prefix)
prefix = "";
if (!(p = get_cgroup_path(name)))
if (!(p = get_cgroup_path(path)))
return -ENOMEM;
r = asprintf(&fn, "%s/cgroup.procs", p);
......@@ -90,14 +94,7 @@ static int show_cgroup_full(const char *name, const char *prefix, unsigned n_col
if (!f)
return -errno;
while (!feof(f)) {
unsigned long ul;
if (fscanf(f, "%lu", &ul) != 1)
break;
if (ul <= 0)
continue;
while ((r = cg_read_pid(f, &pid)) > 0) {
if (n >= n_allocated) {
pid_t *npids;
......@@ -113,12 +110,15 @@ static int show_cgroup_full(const char *name, const char *prefix, unsigned n_col
}
assert(n < n_allocated);
pids[n++] = (pid_t) ul;
pids[n++] = pid;
if (ul > biggest)
biggest = ul;
if (pid > biggest)
biggest = pid;
}
if (r < 0)
goto finish;
if (n > 0) {
unsigned i, m;
......@@ -171,11 +171,11 @@ finish:
return r;
}
int show_cgroup(const char *name, const char *prefix, unsigned n_columns) {
return show_cgroup_full(name, prefix, n_columns, false);
int show_cgroup(const char *path, const char *prefix, unsigned n_columns) {
return show_cgroup_full(path, prefix, n_columns, false);
}
int show_cgroup_recursive(const char *name, const char *prefix, unsigned n_columns) {
int show_cgroup_recursive(const char *path, const char *prefix, unsigned n_columns) {
DIR *d;
char *last = NULL;
char *p1 = NULL, *p2 = NULL, *fn = NULL;
......@@ -183,15 +183,13 @@ int show_cgroup_recursive(const char *name, const char *prefix, unsigned n_colum
bool shown_pids = false;
int r;
assert(name);
if (n_columns <= 0)
n_columns = columns();
if (!prefix)
prefix = "";
if (!(fn = get_cgroup_path(name)))
if (!(fn = get_cgroup_path(path)))
return -ENOMEM;
if (!(d = opendir(fn))) {
......@@ -208,7 +206,7 @@ int show_cgroup_recursive(const char *name, const char *prefix, unsigned n_colum
continue;
if (!shown_pids) {
show_cgroup_full(name, prefix, n_columns, true);
show_cgroup_full(path, prefix, n_columns, true);
shown_pids = true;
}
......@@ -227,14 +225,14 @@ int show_cgroup_recursive(const char *name, const char *prefix, unsigned n_colum
last = NULL;
}
if (asprintf(&last, "%s/%s", name, de->d_name) < 0) {
if (asprintf(&last, "%s/%s", strempty(path), de->d_name) < 0) {
r = -ENOMEM;
goto finish;
}
}
if (!shown_pids)
show_cgroup_full(name, prefix, n_columns, !!last);
show_cgroup_full(path, prefix, n_columns, !!last);
if (last) {
printf("%s\342\224\224 %s\n", prefix, file_name_from_path(last));
......
......@@ -22,7 +22,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
int show_cgroup(const char *name, const char *prefix, unsigned columns);
int show_cgroup_recursive(const char *name, const char *prefix, unsigned columns);
int show_cgroup(const char *path, const char *prefix, unsigned columns);
int show_cgroup_recursive(const char *path, const char *prefix, unsigned columns);
#endif
......@@ -33,6 +33,15 @@
#include "macro.h"
#include "util.h"
/*
Currently, the only remaining functionality from libcgroup we call
here is:
- cgroup_get_subsys_mount_point()
- cgroup_walk_tree_begin()/cgroup_walk_tree_next()
- cgroup_delete_cgroup_ext()
*/
int cg_translate_error(int error, int _errno) {
switch (error) {
......@@ -73,11 +82,78 @@ static struct cgroup* cg_new(const char *controller, const char *path) {
return cgroup;
}
int cg_enumerate_processes(const char *controller, const char *path, FILE **_f) {
char *fs;
int r;
FILE *f;
assert(controller);
assert(path);
assert(_f);
if ((r = cg_get_path(controller, path, "cgroup.procs", &fs)) < 0)
return r;
f = fopen(fs, "re");
free(fs);
if (!f)
return -errno;
*_f = f;
return 0;
}
int cg_enumerate_tasks(const char *controller, const char *path, FILE **_f) {
char *fs;
int r;
FILE *f;
assert(controller);
assert(path);
assert(_f);
if ((r = cg_get_path(controller, path, "tasks", &fs)) < 0)
return r;
f = fopen(fs, "re");
free(fs);
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. */
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 ignore_self) {
bool killed = false, done = false;
Set *s;
pid_t my_pid;
int r, ret = 0;
FILE *f = NULL;
assert(controller);
assert(path);
......@@ -93,23 +169,22 @@ int cg_kill(const char *controller, const char *path, int sig, bool ignore_self)
my_pid = getpid();
do {
void *iterator = NULL;
pid_t pid = 0;
pid_t pid;
done = true;
r = cgroup_get_task_begin(path, controller, &iterator, &pid);
while (r == 0) {
if ((r = cg_enumerate_processes(controller, path, &f)) < 0)
goto finish;
while ((r = cg_read_pid(f, &pid)) > 0) {
if (pid == my_pid && ignore_self)
goto next;
continue;
if (set_get(s, INT_TO_PTR(pid)) == INT_TO_PTR(pid))
goto next;
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 && errno != ESRCH) {
if (ret == 0)
ret = -errno;
......@@ -118,22 +193,12 @@ int cg_kill(const char *controller, const char *path, int sig, bool ignore_self)
killed = true;
done = false;
if ((r = set_put(s, INT_TO_PTR(pid))) < 0)
goto loop_exit;
next:
r = cgroup_get_task_next(&iterator, &pid);
if ((r = set_put(s, LONG_TO_PTR(pid))) < 0)
break;
}
if (r == 0 || r == ECGEOF)
r = 0;
else if (r == ECGOTHER && errno == ENOENT)
r = -ESRCH;
else
r = cg_translate_error(r, errno);
loop_exit:
assert_se(cgroup_get_task_end(&iterator) == 0);
fclose(f);
f = NULL;
/* To avoid racing against processes which fork
* quicker than we can kill them we repeat this until
......@@ -141,14 +206,18 @@ int cg_kill(const char *controller, const char *path, int sig, bool ignore_self)
} while (!done && r >= 0);
finish:
set_free(s);
if (ret < 0)
return ret;
if (f)
fclose(f);
if (r < 0)
return r;
if (ret < 0)
return ret;
return !!killed;
}
......@@ -237,63 +306,53 @@ int cg_kill_recursive_and_wait(const char *controller, const char *path) {
int cg_migrate(const char *controller, const char *from, const char *to, bool ignore_self) {
bool migrated = false, done = false;
struct cgroup *dest;
int r, ret = 0;
pid_t my_pid;
FILE *f = NULL;
assert(controller);
assert(from);
assert(to);
if (!(dest = cg_new(controller, to)))
return -ENOMEM;
my_pid = getpid();
do {
void *iterator = NULL;
pid_t pid = 0;
pid_t pid;
done = true;
r = cgroup_get_task_begin(from, controller, &iterator, &pid);
while (r == 0) {
if ((r = cg_enumerate_tasks(controller, from, &f)) < 0)
goto finish;
while ((r = cg_read_pid(f, &pid)) > 0) {
if (pid == my_pid && ignore_self)
goto next;
continue;
if ((r = cgroup_attach_task_pid(dest, pid)) != 0) {
if ((r = cg_attach(controller, to, pid)) < 0) {
if (ret == 0)
r = cg_translate_error(r, errno);
ret = -r;
}
migrated = true;
done = false;
next:
r = cgroup_get_task_next(&iterator, &pid);
}
if (r == 0 || r == ECGEOF)
r = 0;
else if (r == ECGOTHER && errno == ENOENT)
r = -ESRCH;
else
r = cg_translate_error(r, errno);
assert_se(cgroup_get_task_end(&iterator) == 0);
fclose(f);
f = NULL;
} while (!done && r >= 0);
cgroup_free(&dest);
finish:
if (ret < 0)
return ret;
if (f)
fclose(f);
if (r < 0)
return r;
if (ret < 0)
return ret;
return !!migrated;
}
......@@ -354,18 +413,24 @@ int cg_get_path(const char *controller, const char *path, const char *suffix, ch
int r;
assert(controller);
assert(path);
if ((r = cgroup_get_subsys_mount_point(controller, &mp)) != 0)
return cg_translate_error(r, errno);
if (suffix)
if (path && suffix)
r = asprintf(fs, "%s/%s/%s", mp, path, suffix);
else
else if (path)
r = asprintf(fs, "%s/%s", mp, path);
else if (suffix)
r = asprintf(fs, "%s/%s", mp, suffix);
else {
path_kill_slashes(mp);
*fs = mp;
return 0;
}
free(mp);
path_kill_slashes(*fs);
return r < 0 ? -ENOMEM : 0;
}
......@@ -409,83 +474,59 @@ finish:
}
int cg_create(const char *controller, const char *path) {
struct cgroup *cg;
char *fs;
int r;
assert(controller);
assert(path);
if (!(cg = cg_new(controller, path)))
return -ENOMEM;
if ((r = cgroup_create_cgroup(cg, 1)) != 0) {
r = cg_translate_error(r, errno);
goto finish;
}
r = 0;
if ((r = cg_get_path(controller, path, NULL, &fs)) < 0)
return r;
finish:
cgroup_free(&cg);
r = mkdir_p(fs, 0755);
free(fs);
return r;
}
int cg_attach(const char *controller, const char *path, pid_t pid) {
struct cgroup *cg;
char *fs;
int r;
char c[32];
assert(controller);
assert(path);
assert(pid >= 0);
if (!(cg = cg_new(controller, path)))
return -ENOMEM;
if ((r = cg_get_path(controller, path, "tasks", &fs)) < 0)
return r;
if (pid == 0)
pid = getpid();
if ((r = cgroup_attach_task_pid(cg, pid))) {
r = cg_translate_error(r, errno);
goto finish;
}
r = 0;
snprintf(c, sizeof(c), "%lu\n", (unsigned long) pid);
char_array_0(c);
finish:
cgroup_free(&cg);
r = write_one_line_file(fs, c);
free(fs);
return r;
}
int cg_create_and_attach(const char *controller, const char *path, pid_t pid) {
struct cgroup *cg;
int r;
assert(controller);
assert(path);
assert(pid >= 0);
if (!(cg = cg_new(controller, path)))
return -ENOMEM;
if ((r = cgroup_create_cgroup(cg, 1)) != 0) {
r = cg_translate_error(r, errno);
goto finish;
}
if (pid == 0)
pid = getpid();
if ((r = cgroup_attach_task_pid(cg, pid))) {
r = cg_translate_error(r, errno);
goto finish;
}
if ((r = cg_create(controller, path)) < 0)
return r;
r = 0;
if ((r = cg_attach(controller, path, pid)) < 0)
return r;
finish:
cgroup_free(&cg);
/* This does not remove the cgroup on failure */
return r;
}
......@@ -525,40 +566,82 @@ int cg_set_task_access(const char *controller, const char *path, mode_t mode, ui
int cg_get_by_pid(const char *controller, pid_t pid, char **path) {
int r;
char *p = NULL;
FILE *f;
char *fs;
size_t cs;
assert(controller);
assert(pid > 0);
assert(path);
assert(pid >= 0);
if ((r = cgroup_get_current_controller_path(pid, controller, &p)) != 0)
return cg_translate_error(r, errno);
if (pid == 0)
pid = getpid();
assert(p);
if (asprintf(&fs, "/proc/%lu/cgroup", (unsigned long) pid) < 0)
return -ENOMEM;
*path = p;
return 0;
f = fopen(fs, "re");
free(fs);
cs = strlen(controller);
while (!feof(f)) {
char line[LINE_MAX];
char *l;
errno = 0;
if (!(fgets(line, sizeof(line), f))) {
if (feof(f))
break;
r = errno ? -errno : -EIO;
goto finish;
}
truncate_nl(line);