exec: optionally apply cgroup attributes to the cgroups we create

This commit is contained in:
Lennart Poettering
2011-08-20 00:22:02 +02:00
parent 5ed27dbdbf
commit ab1f063390
20 changed files with 663 additions and 38 deletions
+102
View File
@@ -0,0 +1,102 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2011 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 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
General Public License for more details.
You should have received a copy of the GNU General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "cgroup-attr.h"
#include "cgroup-util.h"
#include "list.h"
int cgroup_attribute_apply(CGroupAttribute *a, CGroupBonding *b) {
int r;
char *path = NULL;
char *v = NULL;
assert(a);
b = cgroup_bonding_find_list(b, a->controller);
if (!b)
return 0;
if (a->map_callback) {
r = a->map_callback(a->controller, a->name, a->value, &v);
if (r < 0)
return r;
}
r = cg_get_path(a->controller, b->path, a->name, &path);
if (r < 0) {
free(v);
return r;
}
r = write_one_line_file(path, v ? v : a->value);
if (r < 0)
log_warning("Failed to write '%s' to %s: %s", v ? v : a->value, path, strerror(-r));
free(path);
free(v);
return r;
}
int cgroup_attribute_apply_list(CGroupAttribute *first, CGroupBonding *b) {
CGroupAttribute *a;
int r = 0;
LIST_FOREACH(by_unit, a, first) {
int k;
k = cgroup_attribute_apply(a, b);
if (r == 0)
r = k;
}
return r;
}
CGroupAttribute *cgroup_attribute_find_list(CGroupAttribute *first, const char *controller, const char *name) {
CGroupAttribute *a;
assert(controller);
assert(name);
LIST_FOREACH(by_unit, a, first)
if (streq(a->controller, controller) &&
streq(a->name, name))
return a;
return NULL;
}
static void cgroup_attribute_free(CGroupAttribute *a) {
assert(a);
free(a->controller);
free(a->name);
free(a->value);
free(a);
}
void cgroup_attribute_free_list(CGroupAttribute *first) {
CGroupAttribute *a, *n;
LIST_FOREACH_SAFE(by_unit, a, n, first)
cgroup_attribute_free(a);
}
+49
View File
@@ -0,0 +1,49 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#ifndef foocgroupattrhfoo
#define foocgroupattrhfoo
/***
This file is part of systemd.
Copyright 2011 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 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
General Public License for more details.
You should have received a copy of the GNU General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
typedef struct CGroupAttribute CGroupAttribute;
typedef int (*CGroupAttributeMapCallback)(const char *controller, const char*name, const char *value, char **ret);
#include "unit.h"
#include "cgroup.h"
struct CGroupAttribute {
char *controller;
char *name;
char *value;
CGroupAttributeMapCallback map_callback;
LIST_FIELDS(CGroupAttribute, by_unit);
};
int cgroup_attribute_apply(CGroupAttribute *a, CGroupBonding *b);
int cgroup_attribute_apply_list(CGroupAttribute *first, CGroupBonding *b);
CGroupAttribute *cgroup_attribute_find_list(CGroupAttribute *first, const char *controller, const char *name);
void cgroup_attribute_free_list(CGroupAttribute *first);
#endif
+4 -1
View File
@@ -41,8 +41,11 @@ int cgroup_bonding_realize(CGroupBonding *b) {
if (b->realized)
return 0;
if ((r = cg_create(b->controller, b->path)) < 0)
r = cg_create(b->controller, b->path);
if (r < 0) {
log_warning("Failed to create cgroup %s:%s: %s", b->controller, b->path, strerror(-r));
return r;
}
b->realized = true;
+6 -3
View File
@@ -930,6 +930,7 @@ int exec_spawn(ExecCommand *command,
bool apply_tty_stdin,
bool confirm_spawn,
CGroupBonding *cgroup_bondings,
CGroupAttribute *cgroup_attributes,
pid_t *ret) {
pid_t pid;
@@ -973,9 +974,11 @@ int exec_spawn(ExecCommand *command,
log_debug("About to execute: %s", line);
free(line);
if (cgroup_bondings)
if ((r = cgroup_bonding_realize_list(cgroup_bondings)))
goto fail_parent;
r = cgroup_bonding_realize_list(cgroup_bondings);
if (r < 0)
goto fail_parent;
cgroup_attribute_apply_list(cgroup_attributes, cgroup_bondings);
if ((pid = fork()) < 0) {
r = -errno;
+2
View File
@@ -35,6 +35,7 @@ typedef struct ExecContext ExecContext;
#include <sched.h>
struct CGroupBonding;
struct CGroupAttribute;
#include "list.h"
#include "util.h"
@@ -187,6 +188,7 @@ int exec_spawn(ExecCommand *command,
bool apply_tty_stdin,
bool confirm_spawn,
struct CGroupBonding *cgroup_bondings,
struct CGroupAttribute *cgroup_attributes,
pid_t *ret);
void exec_command_done(ExecCommand *c);
+1 -1
View File
@@ -70,7 +70,7 @@ int kmod_setup(void) {
command.argv = (char**) cmdline;
exec_context_init(&context);
r = exec_spawn(&command, NULL, &context, NULL, 0, NULL, false, false, false, false, NULL, &pid);
r = exec_spawn(&command, NULL, &context, NULL, 0, NULL, false, false, false, false, NULL, NULL, &pid);
exec_context_done(&context);
if (r < 0) {
+7 -1
View File
@@ -64,7 +64,13 @@ $1.LimitMSGQUEUE, config_parse_limit, RLIMIT_MSGQ
$1.LimitNICE, config_parse_limit, RLIMIT_NICE, offsetof($1, exec_context.rlimit)
$1.LimitRTPRIO, config_parse_limit, RLIMIT_RTPRIO, offsetof($1, exec_context.rlimit)
$1.LimitRTTIME, config_parse_limit, RLIMIT_RTTIME, offsetof($1, exec_context.rlimit)
$1.ControlGroup, config_parse_unit_cgroup, 0, offsetof($1, exec_context)
$1.ControlGroup, config_parse_unit_cgroup, 0, 0
$1.ControlGroupAttribute, config_parse_unit_cgroup_attr, 0, 0
$1.CPUShares, config_parse_unit_cpu_shares, 0, 0
$1.MemoryLimit, config_parse_unit_memory_limit, 0, 0
$1.MemorySoftLimit, config_parse_unit_memory_limit, 0, 0
$1.DeviceAllow, config_parse_unit_device_allow, 0, 0
$1.DeviceDeny, config_parse_unit_device_allow, 0, 0
$1.ReadWriteDirectories, config_parse_path_strv, 0, offsetof($1, exec_context.read_write_dirs)
$1.ReadOnlyDirectories, config_parse_path_strv, 0, offsetof($1, exec_context.read_only_dirs)
$1.InaccessibleDirectories, config_parse_path_strv, 0, offsetof($1, exec_context.inaccessible_dirs)
+195
View File
@@ -1639,6 +1639,201 @@ int config_parse_unit_condition_null(
DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier");
int config_parse_unit_cgroup_attr(
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
Unit *u = data;
char **l;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
l = strv_split_quoted(rvalue);
if (!l)
return -ENOMEM;
if (strv_length(l) != 2) {
log_error("[%s:%u] Failed to parse cgroup attribute value, ignoring: %s", filename, line, rvalue);
strv_free(l);
return 0;
}
r = unit_add_cgroup_attribute(u, NULL, l[0], l[1], NULL);
strv_free(l);
if (r < 0) {
log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
return 0;
}
return 0;
}
int config_parse_unit_cpu_shares(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) {
Unit *u = data;
int r;
unsigned long ul;
char *t;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
if (safe_atolu(rvalue, &ul) < 0 || ul < 1) {
log_error("[%s:%u] Failed to parse CPU shares value, ignoring: %s", filename, line, rvalue);
return 0;
}
if (asprintf(&t, "%lu", ul) < 0)
return -ENOMEM;
r = unit_add_cgroup_attribute(u, "cpu", "cpu.shares", t, NULL);
free(t);
if (r < 0) {
log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
return 0;
}
return 0;
}
int config_parse_unit_memory_limit(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) {
Unit *u = data;
int r;
off_t sz;
char *t;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
if (parse_bytes(rvalue, &sz) < 0 || sz <= 0) {
log_error("[%s:%u] Failed to parse memory limit value, ignoring: %s", filename, line, rvalue);
return 0;
}
if (asprintf(&t, "%llu", (unsigned long long) sz) < 0)
return -ENOMEM;
r = unit_add_cgroup_attribute(u,
"memory",
streq(lvalue, "MemorySoftLimit") ? "memory.soft_limit_in_bytes" : "memory.limit_in_bytes",
t, NULL);
free(t);
if (r < 0) {
log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
return 0;
}
return 0;
}
static int device_map(const char *controller, const char *name, const char *value, char **ret) {
struct stat st;
char **l;
l = strv_split_quoted(value);
if (!l)
return -ENOMEM;
assert(strv_length(l) >= 1);
if (streq(l[0], "*")) {
if (asprintf(ret, "a *:*%s%s",
isempty(l[1]) ? "" : " ", strempty(l[1])) < 0) {
strv_free(l);
return -ENOMEM;
}
} else {
if (lstat(l[0], &st) < 0) {
log_warning("Couldn't stat device %s", l[0]);
strv_free(l);
return -errno;
}
if (!S_ISCHR(st.st_mode) && !S_ISBLK(st.st_mode)) {
log_warning("%s is not a device.", l[0]);
strv_free(l);
return -ENODEV;
}
if (asprintf(ret, "%c %u:%u%s%s",
S_ISCHR(st.st_mode) ? 'c' : 'b',
major(st.st_rdev), minor(st.st_rdev),
isempty(l[1]) ? "" : " ", strempty(l[1])) < 0) {
strv_free(l);
return -ENOMEM;
}
}
strv_free(l);
return 0;
}
int config_parse_unit_device_allow(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) {
Unit *u = data;
char **l;
int r;
unsigned k;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
l = strv_split_quoted(rvalue);
if (!l)
return -ENOMEM;
k = strv_length(l);
if (k < 1 || k > 2) {
log_error("[%s:%u] Failed to parse device value, ignoring: %s", filename, line, rvalue);
strv_free(l);
return 0;
}
if (!streq(l[0], "*") && !path_startswith(l[0], "/dev")) {
log_error("[%s:%u] Device node path not absolute, ignoring: %s", filename, line, rvalue);
strv_free(l);
return 0;
}
if (!isempty(l[1]) && !in_charset(l[1], "rwm")) {
log_error("[%s:%u] Device access string invalid, ignoring: %s", filename, line, rvalue);
strv_free(l);
return 0;
}
strv_free(l);
r = unit_add_cgroup_attribute(u, "devices",
streq(lvalue, "DeviceAllow") ? "devices.allow" : "devices.deny",
rvalue, device_map);
if (r < 0) {
log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
return 0;
}
return 0;
}
#define FOLLOW_MAX 8
static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
+4
View File
@@ -76,6 +76,10 @@ int config_parse_unit_condition_string(const char *filename, unsigned line, cons
int config_parse_unit_condition_null(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_kill_mode(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_notify_access(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_unit_cgroup_attr(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_unit_cpu_shares(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_unit_memory_limit(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_unit_device_allow(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
/* gperf prototypes */
const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, unsigned length);
+1
View File
@@ -791,6 +791,7 @@ static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) {
true,
m->meta.manager->confirm_spawn,
m->meta.cgroup_bondings,
m->meta.cgroup_attributes,
&pid)) < 0)
goto fail;
+1
View File
@@ -1728,6 +1728,7 @@ static int service_spawn(
apply_tty_stdin,
s->meta.manager->confirm_spawn,
s->meta.cgroup_bondings,
s->meta.cgroup_attributes,
&pid);
if (r < 0)
+1
View File
@@ -1112,6 +1112,7 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) {
true,
s->meta.manager->confirm_spawn,
s->meta.cgroup_bondings,
s->meta.cgroup_attributes,
&pid);
strv_free(argv);
+1
View File
@@ -613,6 +613,7 @@ static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) {
true,
s->meta.manager->confirm_spawn,
s->meta.cgroup_bondings,
s->meta.cgroup_attributes,
&pid)) < 0)
goto fail;
+78 -3
View File
@@ -42,6 +42,7 @@
#include "special.h"
#include "cgroup-util.h"
#include "missing.h"
#include "cgroup-attr.h"
const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX] = {
[UNIT_SERVICE] = &service_vtable,
@@ -372,6 +373,7 @@ void unit_free(Unit *u) {
}
cgroup_bonding_free_list(u->meta.cgroup_bondings, u->meta.manager->n_reloading <= 0);
cgroup_attribute_free_list(u->meta.cgroup_attributes);
free(u->meta.description);
free(u->meta.fragment_path);
@@ -592,7 +594,6 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
Iterator i;
char *p2;
const char *prefix2;
CGroupBonding *b;
char
timestamp1[FORMAT_TIMESTAMP_MAX],
timestamp2[FORMAT_TIMESTAMP_MAX],
@@ -662,6 +663,9 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
}
if (u->meta.load_state == UNIT_LOADED) {
CGroupBonding *b;
CGroupAttribute *a;
fprintf(f,
"%s\tStopWhenUnneeded: %s\n"
"%s\tRefuseManualStart: %s\n"
@@ -682,6 +686,10 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
fprintf(f, "%s\tControlGroup: %s:%s\n",
prefix, b->controller, b->path);
LIST_FOREACH(by_unit, a, u->meta.cgroup_attributes)
fprintf(f, "%s\tControlGroupAttribute: %s %s \"%s\"\n",
prefix, a->controller, a->name, a->value);
if (UNIT_VTABLE(u)->dump)
UNIT_VTABLE(u)->dump(u, f, prefix2);
@@ -1885,8 +1893,10 @@ fail:
}
int unit_add_default_cgroups(Unit *u) {
CGroupAttribute *a;
char **c;
int r;
assert(u);
/* Adds in the default cgroups, if they weren't specified
@@ -1899,8 +1909,10 @@ int unit_add_default_cgroups(Unit *u) {
return r;
STRV_FOREACH(c, u->meta.manager->default_controllers)
if ((r = unit_add_one_default_cgroup(u, *c)) < 0)
return r;
unit_add_one_default_cgroup(u, *c);
LIST_FOREACH(by_unit, a, u->meta.cgroup_attributes)
unit_add_one_default_cgroup(u, a->controller);
return 0;
}
@@ -1911,6 +1923,69 @@ CGroupBonding* unit_get_default_cgroup(Unit *u) {
return cgroup_bonding_find_list(u->meta.cgroup_bondings, SYSTEMD_CGROUP_CONTROLLER);
}
int unit_add_cgroup_attribute(Unit *u, const char *controller, const char *name, const char *value, CGroupAttributeMapCallback map_callback) {
int r;
char *c = NULL;
CGroupAttribute *a;
assert(u);
assert(name);
assert(value);
if (!controller) {
const char *dot;
dot = strchr(name, '.');
if (!dot)
return -EINVAL;
c = strndup(name, dot - name);
if (!c)
return -ENOMEM;
controller = c;
}
if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
r = -EINVAL;
goto finish;
}
a = new0(CGroupAttribute, 1);
if (!a) {
r = -ENOMEM;
goto finish;
}
if (c) {
a->controller = c;
c = NULL;
} else
a->controller = strdup(controller);
a->name = strdup(name);
a->value = strdup(value);
if (!a->controller || !a->name || !a->value) {
free(a->controller);
free(a->name);
free(a->value);
free(a);
return -ENOMEM;
}
a->map_callback = map_callback;
LIST_PREPEND(CGroupAttribute, by_unit, u->meta.cgroup_attributes, a);
r = 0;
finish:
free(c);
return r;
}
int unit_load_related_unit(Unit *u, const char *type, Unit **_found) {
char *t;
int r;
+3
View File
@@ -130,6 +130,7 @@ enum UnitDependency {
#include "manager.h"
#include "job.h"
#include "cgroup.h"
#include "cgroup-attr.h"
struct Meta {
Manager *manager;
@@ -167,6 +168,7 @@ struct Meta {
/* Counterparts in the cgroup filesystem */
CGroupBonding *cgroup_bondings;
CGroupAttribute *cgroup_attributes;
/* Per type list */
LIST_FIELDS(Meta, units_by_type);
@@ -432,6 +434,7 @@ int unit_add_cgroup(Unit *u, CGroupBonding *b);
int unit_add_cgroup_from_text(Unit *u, const char *name);
int unit_add_default_cgroups(Unit *u);
CGroupBonding* unit_get_default_cgroup(Unit *u);
int unit_add_cgroup_attribute(Unit *u, const char *controller, const char *name, const char *value, CGroupAttributeMapCallback map_callback);
int unit_choose_id(Unit *u, const char *name);
int unit_set_description(Unit *u, const char *description);
+69
View File
@@ -1439,6 +1439,19 @@ char *delete_chars(char *s, const char *bad) {
return s;
}
bool in_charset(const char *s, const char* charset) {
const char *i;
assert(s);
assert(charset);
for (i = s; *i; i++)
if (!strchr(charset, *i))
return false;
return true;
}
char *file_in_same_dir(const char *path, const char *filename) {
char *e, *r;
size_t k;
@@ -2969,6 +2982,62 @@ int parse_usec(const char *t, usec_t *usec) {
return 0;
}
int parse_bytes(const char *t, off_t *bytes) {
static const struct {
const char *suffix;
off_t factor;
} table[] = {
{ "B", 1 },
{ "K", 1024ULL },
{ "M", 1024ULL*1024ULL },
{ "G", 1024ULL*1024ULL*1024ULL },
{ "T", 1024ULL*1024ULL*1024ULL*1024ULL },
{ "", 1 },
};
const char *p;
off_t r = 0;
assert(t);
assert(bytes);
p = t;
do {
long long l;
char *e;
unsigned i;
errno = 0;
l = strtoll(p, &e, 10);
if (errno != 0)
return -errno;
if (l < 0)
return -ERANGE;
if (e == p)
return -EINVAL;
e += strspn(e, WHITESPACE);
for (i = 0; i < ELEMENTSOF(table); i++)
if (startswith(e, table[i].suffix)) {
r += (off_t) l * table[i].factor;
p = e + strlen(table[i].suffix);
break;
}
if (i >= ELEMENTSOF(table))
return -EINVAL;
} while (*p != 0);
*bytes = r;
return 0;
}
int make_stdio(int fd) {
int r, s, t;
+3
View File
@@ -135,6 +135,7 @@ void close_many(const int fds[], unsigned n_fd);
int parse_boolean(const char *v);
int parse_usec(const char *t, usec_t *usec);
int parse_bytes(const char *t, off_t *bytes);
int parse_pid(const char *s, pid_t* ret_pid);
int parse_uid(const char *s, uid_t* ret_uid);
#define parse_gid(s, ret_uid) parse_uid(s, ret_uid)
@@ -462,6 +463,8 @@ char *join(const char *x, ...) _sentinel_;
bool is_main_thread(void);
bool in_charset(const char *s, const char* charset);
#define NULSTR_FOREACH(i, l) \
for ((i) = (l); (i) && *(i); (i) = strchr((i), 0)+1)