cgroup-util.c revision 5f4c5fef66581383ee852b301db67f687663004c
b76e85d5549ba6fc5535ec2a590bad9443bb14d8Ken Stubbings/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
eaf6de42255b7dab8d5531d29fffc299b3aaddd4Robert Wapshott This file is part of systemd.
eaf6de42255b7dab8d5531d29fffc299b3aaddd4Robert Wapshott Copyright 2010 Lennart Poettering
eaf6de42255b7dab8d5531d29fffc299b3aaddd4Robert Wapshott systemd is free software; you can redistribute it and/or modify it
eaf6de42255b7dab8d5531d29fffc299b3aaddd4Robert Wapshott under the terms of the GNU Lesser General Public License as published by
eaf6de42255b7dab8d5531d29fffc299b3aaddd4Robert Wapshott the Free Software Foundation; either version 2.1 of the License, or
eaf6de42255b7dab8d5531d29fffc299b3aaddd4Robert Wapshott (at your option) any later version.
eaf6de42255b7dab8d5531d29fffc299b3aaddd4Robert Wapshott systemd is distributed in the hope that it will be useful, but
eaf6de42255b7dab8d5531d29fffc299b3aaddd4Robert Wapshott WITHOUT ANY WARRANTY; without even the implied warranty of
eaf6de42255b7dab8d5531d29fffc299b3aaddd4Robert Wapshott MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
eaf6de42255b7dab8d5531d29fffc299b3aaddd4Robert Wapshott Lesser General Public License for more details.
f370eece69b53591e104fab667134cfe8d96fabdNathaniel McCallum You should have received a copy of the GNU Lesser General Public License
f370eece69b53591e104fab667134cfe8d96fabdNathaniel McCallum along with systemd; If not, see <http://www.gnu.org/licenses/>.
#include <signal.h>
#include <string.h>
#include <stdlib.h>
#include <dirent.h>
#include <ftw.h>
#include "set.h"
#include "macro.h"
#include "util.h"
#include "formats-util.h"
#include "process-util.h"
#include "path-util.h"
#include "unit-name.h"
#include "fileio.h"
#include "special.h"
#include "mkdir.h"
#include "login-util.h"
#include "cgroup-util.h"
FILE *f;
return -errno;
*_f = f;
unsigned long ul;
* cgroups.txt for details. */
assert(f);
errno = 0;
if (feof(f))
if (ul <= 0)
return -EIO;
DIR *d;
return -errno;
*_d = d;
assert(d);
return -ENOMEM;
*fn = b;
r = rmdir(p);
return -errno;
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;
return -ENOMEM;
done = true;
return ret;
if (ret == 0)
done = false;
if (ret >= 0)
return ret;
if (ret >= 0)
return ret;
} while (!done);
return ret;
int cg_kill_recursive(const char *controller, const char *path, int sig, bool sigcont, bool ignore_self, bool rem, Set *s) {
int r, ret;
char *fn;
return -ENOMEM;
return ret;
return -ENOMEM;
if (r != 0 && ret >= 0)
ret = r;
if (ret >= 0 && r < 0)
ret = r;
if (rem) {
return ret;
int cg_migrate(const char *cfrom, const char *pfrom, const char *cto, const char *pto, bool ignore_self) {
bool done = false;
int r, ret = 0;
return -ENOMEM;
done = true;
return ret;
if (cfrom &&
ret = r;
} else if (ret == 0)
done = false;
if (ret >= 0)
return ret;
if (ret >= 0)
return ret;
} while (!done);
return ret;
int cg_migrate_recursive(
const char *cfrom,
const char *pfrom,
const char *cto,
const char *pto,
bool ignore_self,
bool rem) {
int r, ret = 0;
char *fn;
return ret;
return -ENOMEM;
if (r != 0 && ret >= 0)
ret = r;
if (r < 0 && ret >= 0)
ret = r;
if (rem) {
return ret;
const char *cfrom,
const char *pfrom,
const char *cto,
const char *pto,
bool ignore_self,
bool rem) {
return controller;
static int join_path_legacy(const char *controller, const char *path, const char *suffix, char **fs) {
const char *dn;
char *t = NULL;
return -ENOMEM;
*fs = t;
return -ENOMEM;
*fs = t;
int unified, r;
if (!controller) {
return -EINVAL;
if (!suffix)
else if (!path)
return -ENOMEM;
return -EINVAL;
if (unified < 0)
return unified;
if (unified > 0)
int unified;
return -EINVAL;
if (unified < 0)
return unified;
if (unified > 0) {
return -EOPNOTSUPP;
return -errno;
int cg_get_path_and_check(const char *controller, const char *path, const char *suffix, char **fs) {
errno = 0;
else if (errno != 0)
r = -errno;
r = -EIO;
if (delete_root) {
return -errno;
return -errno;
if (pid == 0)
int cg_set_group_access(
const char *controller,
const char *path,
int cg_set_task_access(
const char *controller,
const char *path,
int r, unified;
if (unified < 0)
return unified;
if (unified)
const char *fs;
int unified;
if (unified < 0)
return unified;
if (unified == 0) {
if (controller) {
return -EINVAL;
if (unified) {
size_t k;
bool found = false;
found = true;
if (!found)
return -ENOMEM;
*path = p;
return -ENODATA;
const char *sc;
int r, unified;
if (unified < 0)
return unified;
return -EOPNOTSUPP;
return -EEXIST;
return -EIO;
int r, unified;
if (unified < 0)
return unified;
return -EOPNOTSUPP;
if (r == -ENOENT)
int unified, r;
if (unified < 0)
return unified;
if (unified > 0) {
if (r == -ENOENT)
char *fn;
if (r == -ENOENT)
return -ENOMEM;
return -EINVAL;
if (path) {
return -ENOMEM;
if (controller)
return -EINVAL;
if (controller) {
return -ENOMEM;
*controller = t;
if (path)
return -ENOMEM;
if (!cg_controller_is_valid(t)) {
free(t);
return -EINVAL;
u = NULL;
free(t);
return -ENOMEM;
if (!path_is_safe(u) ||
!path_is_absolute(u)) {
free(t);
free(u);
return -EINVAL;
if (controller)
*controller = t;
free(t);
if (path)
*path = u;
free(u);
return -ENOMEM;
*path = p;
if (!root) {
if (p && p > cgroup)
if (c == raw) {
n = strdup(c);
return -ENOMEM;
*cgroup = n;
size_t n;
return -ENXIO;
c = cg_unescape(c);
return -ENXIO;
s = strdup(c);
return -ENOMEM;
*unit = s;
buf[n] = 0;
static const char *skip_slices(const char *p) {
assert(p);
size_t n;
if (!valid_slice_name(p, n))
char *unit;
return -ENXIO;
static const char *skip_session(const char *p) {
size_t n;
if (isempty(p))
return NULL;
return NULL;
return NULL;
static const char *skip_user_manager(const char *p) {
size_t n;
if (isempty(p))
return NULL;
return NULL;
return NULL;
return NULL;
t = skip_user_manager(e);
return skip_session(e);
return -ENXIO;
const char *sl;
if (!start)
return -ENXIO;
if (!end)
return -ENXIO;
*end = 0;
return -ENXIO;
if (session) {
char *rr;
if (!rr)
return -ENOMEM;
if (!start)
return -ENXIO;
if (!end)
return -ENXIO;
*end = 0;
return -ENXIO;
const char *e = NULL;
assert(p);
size_t n;
if (!valid_slice_name(p, n)) {
return -ENOMEM;
*slice = s;
assert(p);
t = skip_user_prefix(p);
return -ENXIO;
char *cg_escape(const char *p) {
bool need_prefix = false;
need_prefix = true;
const char *dot;
if (dot) {
for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
n = cgroup_controller_to_string(c);
if (l != strlen(n))
if (memcmp(p, n, l) != 0)
need_prefix = true;
if (need_prefix)
return strdup(p);
char *cg_unescape(const char *p) {
assert(p);
#define CONTROLLER_VALID \
bool cg_controller_is_valid(const char *p) {
if (t - p > FILENAME_MAX)
const char *dash;
return -ENOMEM;
*ret = x;
return -EINVAL;
return -EINVAL;
if (dash == p)
return -EINVAL;
while (dash) {
return -EINVAL;
return -EINVAL;
if (!escaped)
return -ENOMEM;
return -ENOMEM;
return -ENOMEM;
return -ENOMEM;
*ret = s;
s = NULL;
int cg_set_attribute(const char *controller, const char *path, const char *attribute, const char *value) {
int r, unified;
if (unified < 0)
return unified;
if (unified > 0)
for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
n = cgroup_controller_to_string(c);
int cg_attach_everywhere(CGroupMask supported, const char *path, pid_t pid, cg_migrate_callback_t path_callback, void *userdata) {
int r, unified;
if (unified < 0)
return unified;
if (unified > 0)
for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
const char *p = NULL;
if (path_callback)
p = path;
int cg_attach_many_everywhere(CGroupMask supported, const char *path, Set* pids, cg_migrate_callback_t path_callback, void *userdata) {
Iterator i;
void *pidp;
int cg_migrate_everywhere(CGroupMask supported, const char *from, const char *to, cg_migrate_callback_t to_callback, void *userdata) {
int r = 0, unified;
r = cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, from, SYSTEMD_CGROUP_CONTROLLER, to, false, true);
if (unified < 0)
return unified;
if (unified > 0)
for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
const char *p = NULL;
if (to_callback)
p = to;
(void) cg_migrate_recursive_fallback(SYSTEMD_CGROUP_CONTROLLER, to, cgroup_controller_to_string(c), p, false, false);
int r, unified;
if (unified < 0)
return unified;
if (unified > 0)
for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
int r, unified;
if (unified < 0)
return unified;
if (unified > 0) {
c = controllers;
v = cgroup_controller_from_string(n);
for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
n = cgroup_controller_to_string(c);
if (controller_is_accessible(n) >= 0)
return -errno;
char *controller;
int enabled = 0;
errno = 0;
if (feof(f))
return -errno;
return -EBADMSG;
if (!enabled) {
return -EBADMSG;
int cg_unified(void) {
if (unified_cache >= 0)
return unified_cache;
return -errno;
unified_cache = true;
unified_cache = false;
return -ENOEXEC;
return unified_cache;
void cg_unified_flush(void) {
int r, unified;
assert(p);
if (supported == 0)
if (unified < 0)
return unified;
for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
n = cgroup_controller_to_string(c);
bool cg_is_unified_wanted(void) {
int r, unified;
if (unified >= 0)
return unified;
if (wanted >= 0)
return wanted;
return (wanted = true);
return (wanted = false);
bool cg_is_legacy_wanted(void) {
return !cg_is_unified_wanted();