unit: rework resource management API

This introduces a new static list of known attributes and their special
semantics. This means that cgroup attribute values can now be
automatically translated from user to kernel notation for command line
set settings, too.

This also adds proper support for multi-line attributes.
This commit is contained in:
Lennart Poettering 2013-02-27 18:50:41 +01:00
parent 416389f7ae
commit 26d04f86a3
17 changed files with 915 additions and 679 deletions

View File

@ -852,6 +852,8 @@ libsystemd_core_la_SOURCES = \
src/core/tcpwrap.h \
src/core/cgroup-attr.c \
src/core/cgroup-attr.h \
src/core/cgroup-semantics.c \
src/core/cgroup-semantics.h \
src/core/securebits.h \
src/core/initreq.h \
src/core/special.h \

View File

@ -25,8 +25,8 @@
#include "fileio.h"
int cgroup_attribute_apply(CGroupAttribute *a, CGroupBonding *b) {
int r;
_cleanup_free_ char *path = NULL, *v = NULL;
int r;
assert(a);
@ -34,8 +34,8 @@ int cgroup_attribute_apply(CGroupAttribute *a, CGroupBonding *b) {
if (!b)
return 0;
if (a->map_callback) {
r = a->map_callback(a->controller, a->name, a->value, &v);
if (a->semantics && a->semantics->map_write) {
r = a->semantics->map_write(a->semantics, a->value, &v);
if (r < 0)
return r;
}
@ -66,6 +66,29 @@ int cgroup_attribute_apply_list(CGroupAttribute *first, CGroupBonding *b) {
return r;
}
bool cgroup_attribute_matches(CGroupAttribute *a, const char *controller, const char *name) {
assert(a);
if (controller) {
if (streq(a->controller, controller) && (!name || streq(a->name, name)))
return true;
} else if (!name)
return true;
else if (streq(a->name, name)) {
size_t x, y;
x = strlen(a->controller);
y = strlen(name);
if (y > x &&
memcmp(a->controller, name, x) == 0 &&
name[x] == '.')
return true;
}
return false;
}
CGroupAttribute *cgroup_attribute_find_list(
CGroupAttribute *first,
const char *controller,
@ -74,24 +97,9 @@ CGroupAttribute *cgroup_attribute_find_list(
assert(name);
LIST_FOREACH(by_unit, a, first) {
if (controller) {
if (streq(a->controller, controller) && streq(a->name, name))
return a;
} else if (streq(a->name, name)) {
size_t x, y;
x = strlen(a->controller);
y = strlen(name);
if (y > x &&
memcmp(a->controller, name, x) == 0 &&
name[x] == '.')
return a;
}
}
LIST_FOREACH(by_unit, a, first)
if (cgroup_attribute_matches(a, controller, name))
return a;
return NULL;
}
@ -114,3 +122,11 @@ void cgroup_attribute_free_list(CGroupAttribute *first) {
LIST_FOREACH_SAFE(by_unit, a, n, first)
cgroup_attribute_free(a);
}
void cgroup_attribute_free_some(CGroupAttribute *first, const char *controller, const char *name) {
CGroupAttribute *a, *n;
LIST_FOREACH_SAFE(by_unit, a, n, first)
if (cgroup_attribute_matches(a, controller, name))
cgroup_attribute_free(a);
}

View File

@ -23,10 +23,9 @@
typedef struct CGroupAttribute CGroupAttribute;
typedef int (*CGroupAttributeMapCallback)(const char *controller, const char*name, const char *value, char **ret);
#include "unit.h"
#include "cgroup.h"
#include "cgroup-semantics.h"
struct CGroupAttribute {
char *controller;
@ -35,7 +34,7 @@ struct CGroupAttribute {
Unit *unit;
CGroupAttributeMapCallback map_callback;
const CGroupSemantics *semantics;
LIST_FIELDS(CGroupAttribute, by_unit);
};
@ -43,7 +42,9 @@ struct CGroupAttribute {
int cgroup_attribute_apply(CGroupAttribute *a, CGroupBonding *b);
int cgroup_attribute_apply_list(CGroupAttribute *first, CGroupBonding *b);
bool cgroup_attribute_matches(CGroupAttribute *a, const char *controller, const char *name);
CGroupAttribute *cgroup_attribute_find_list(CGroupAttribute *first, const char *controller, const char *name);
void cgroup_attribute_free(CGroupAttribute *a);
void cgroup_attribute_free_list(CGroupAttribute *first);
void cgroup_attribute_free_some(CGroupAttribute *first, const char *controller, const char *name);

333
src/core/cgroup-semantics.c Normal file
View File

@ -0,0 +1,333 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2013 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 <http://www.gnu.org/licenses/>.
***/
#include "util.h"
#include "strv.h"
#include "path-util.h"
#include "cgroup-util.h"
#include "cgroup-semantics.h"
static int parse_cpu_shares(const CGroupSemantics *s, const char *value, char **ret) {
unsigned long ul;
assert(s);
assert(value);
assert(ret);
if (safe_atolu(value, &ul) < 0 || ul < 1)
return -EINVAL;
if (asprintf(ret, "%lu", ul) < 0)
return -ENOMEM;
return 1;
}
static int parse_memory_limit(const CGroupSemantics *s, const char *value, char **ret) {
off_t sz;
assert(s);
assert(value);
assert(ret);
if (parse_bytes(value, &sz) < 0 || sz <= 0)
return -EINVAL;
if (asprintf(ret, "%llu", (unsigned long long) sz) < 0)
return -ENOMEM;
return 1;
}
static int parse_device(const CGroupSemantics *s, const char *value, char **ret) {
_cleanup_strv_free_ char **l = NULL;
char *x;
unsigned k;
assert(s);
assert(value);
assert(ret);
l = strv_split_quoted(value);
if (!l)
return -ENOMEM;
k = strv_length(l);
if (k < 1 || k > 2)
return -EINVAL;
if (!streq(l[0], "*") && !path_startswith(l[0], "/dev"))
return -EINVAL;
if (!isempty(l[1]) && !in_charset(l[1], "rwm"))
return -EINVAL;
x = strdup(value);
if (!x)
return -ENOMEM;
*ret = x;
return 1;
}
static int parse_blkio_weight(const CGroupSemantics *s, const char *value, char **ret) {
_cleanup_strv_free_ char **l = NULL;
unsigned long ul;
assert(s);
assert(value);
assert(ret);
l = strv_split_quoted(value);
if (!l)
return -ENOMEM;
if (strv_length(l) != 1)
return 0; /* Returning 0 will cause parse_blkio_weight_device() be tried instead */
if (safe_atolu(l[0], &ul) < 0 || ul < 10 || ul > 1000)
return -EINVAL;
if (asprintf(ret, "%lu", ul) < 0)
return -ENOMEM;
return 1;
}
static int parse_blkio_weight_device(const CGroupSemantics *s, const char *value, char **ret) {
_cleanup_strv_free_ char **l = NULL;
unsigned long ul;
assert(s);
assert(value);
assert(ret);
l = strv_split_quoted(value);
if (!l)
return -ENOMEM;
if (strv_length(l) != 2)
return -EINVAL;
if (!path_startswith(l[0], "/dev"))
return -EINVAL;
if (safe_atolu(l[1], &ul) < 0 || ul < 10 || ul > 1000)
return -EINVAL;
if (asprintf(ret, "%s %lu", l[0], ul) < 0)
return -ENOMEM;
return 1;
}
static int parse_blkio_bandwidth(const CGroupSemantics *s, const char *value, char **ret) {
_cleanup_strv_free_ char **l = NULL;
off_t bytes;
assert(s);
assert(value);
assert(ret);
l = strv_split_quoted(value);
if (!l)
return -ENOMEM;
if (strv_length(l) != 2)
return -EINVAL;
if (!path_startswith(l[0], "/dev")) {
return -EINVAL;
}
if (parse_bytes(l[1], &bytes) < 0 || bytes <= 0)
return -EINVAL;
if (asprintf(ret, "%s %llu", l[0], (unsigned long long) bytes) < 0)
return -ENOMEM;
return 0;
}
static int map_device(const CGroupSemantics *s, const char *value, char **ret) {
_cleanup_strv_free_ char **l = NULL;
unsigned k;
assert(s);
assert(value);
assert(ret);
l = strv_split_quoted(value);
if (!l)
return -ENOMEM;
k = strv_length(l);
if (k < 1 || k > 2)
return -EINVAL;
if (streq(l[0], "*")) {
if (asprintf(ret, "a *:*%s%s",
isempty(l[1]) ? "" : " ", strempty(l[1])) < 0)
return -ENOMEM;
} else {
struct stat st;
if (stat(l[0], &st) < 0) {
log_warning("Couldn't stat device %s", l[0]);
return -errno;
}
if (!S_ISCHR(st.st_mode) && !S_ISBLK(st.st_mode)) {
log_warning("%s is not a device.", l[0]);
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)
return -ENOMEM;
}
return 0;
}
static int map_blkio(const CGroupSemantics *s, const char *value, char **ret) {
_cleanup_strv_free_ char **l = NULL;
struct stat st;
dev_t d;
assert(s);
assert(value);
assert(ret);
l = strv_split_quoted(value);
if (!l)
return log_oom();
if (strv_length(l) != 2)
return -EINVAL;
if (stat(l[0], &st) < 0) {
log_warning("Couldn't stat device %s", l[0]);
return -errno;
}
if (S_ISBLK(st.st_mode))
d = st.st_rdev;
else if (major(st.st_dev) != 0) {
/* If this is not a device node then find the block
* device this file is stored on */
d = st.st_dev;
/* If this is a partition, try to get the originating
* block device */
block_get_whole_disk(d, &d);
} else {
log_warning("%s is not a block device and file system block device cannot be determined or is not local.", l[0]);
return -ENODEV;
}
if (asprintf(ret, "%u:%u %s", major(d), minor(d), l[1]) < 0)
return -ENOMEM;
return 0;
}
static const CGroupSemantics semantics[] = {
{ "cpu", "cpu.shares", "CPUShare", false, parse_cpu_shares, NULL, NULL },
{ "memory", "memory.soft_limit_in_bytes", "MemorySoftLimit", false, parse_memory_limit, NULL, NULL },
{ "memory", "memory.limit_in_bytes", "MemoryLimit", false, parse_memory_limit, NULL, NULL },
{ "devices", "devices.allow", "DeviceAllow", true, parse_device, map_device, NULL },
{ "devices", "devices.deny", "DeviceDeny", true, parse_device, map_device, NULL },
{ "blkio", "blkio.weight", "BlockIOWeight", false, parse_blkio_weight, NULL, NULL },
{ "blkio", "blkio.weight_device", "BlockIOWeight", true, parse_blkio_weight_device, map_blkio, NULL },
{ "blkio", "blkio.read_bps_device", "BlockIOReadBandwidth", true, parse_blkio_bandwidth, map_blkio, NULL },
{ "blkio", "blkio.write_bps_device", "BlockIOWriteBandwidth", true, parse_blkio_bandwidth, map_blkio, NULL }
};
int cgroup_semantics_find(
const char *controller,
const char *name,
const char *value,
char **ret,
const CGroupSemantics **_s) {
_cleanup_free_ char *c = NULL;
unsigned i;
int r;
assert(name);
assert(_s);
assert(!value == !ret);
if (!controller) {
r = cg_controller_from_attr(name, &c);
if (r < 0)
return r;
controller = c;
}
for (i = 0; i < ELEMENTSOF(semantics); i++) {
const CGroupSemantics *s = semantics + i;
bool matches_name, matches_pretty;
if (controller && s->controller && !streq(s->controller, controller))
continue;
matches_name = s->name && streq(s->name, name);
matches_pretty = s->pretty && streq(s->pretty, name);
if (!matches_name && !matches_pretty)
continue;
if (value) {
if (matches_pretty && s->map_pretty) {
r = s->map_pretty(s, value, ret);
if (r < 0)
return r;
if (r == 0)
continue;
} else {
char *x;
x = strdup(value);
if (!x)
return -ENOMEM;
*ret = x;
}
}
*_s = s;
return 1;
}
*ret = NULL;
*_s = NULL;
return 0;
}

View File

@ -0,0 +1,43 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#pragma once
/***
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 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 <http://www.gnu.org/licenses/>.
***/
typedef struct CGroupSemantics CGroupSemantics;
struct CGroupSemantics {
const char *controller;
const char *name;
const char *pretty;
bool multiple;
/* This call is used for parsing the pretty value to the actual attribute value */
int (*map_pretty)(const CGroupSemantics *semantics, const char *value, char **ret);
/* Right before writing this attribute the attribute value is converted to a low-level value */
int (*map_write)(const CGroupSemantics *semantics, const char *value, char **ret);
/* If this attribute takes a list, this call can be used to reset the list to empty */
int (*reset)(const CGroupSemantics *semantics, const char *group);
};
int cgroup_semantics_find(const char *controller, const char *name, const char *value, char **ret, const CGroupSemantics **semantics);

View File

@ -103,31 +103,32 @@
" <method name=\"ResetFailedUnit\">\n" \
" <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
" </method>\n" \
" <method name=\"GetUnitControlGroupAttributes\">\n" \
" <method name=\"SetUnitControlGroup\">\n" \
" <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"attributes\" type=\"as\" direction=\"in\"/>\n" \
" <arg name=\"values\" type=\"as\" direction=\"out\"/>\n" \
" <arg name=\"group\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
" </method>\n" \
" <method name=\"SetUnitControlGroupAttributes\">\n" \
" <method name=\"UnsetUnitControlGroup\">\n" \
" <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"attributes\" type=\"a(sss)\" direction=\"in\"/>\n" \
" <arg name=\"group\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"mode\" type=\"s\" direction=\"in\"\n/>" \
" </method>\n" \
" <method name=\"GetUnitControlGroupAttribute\">\n" \
" <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"attribute\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"values\" type=\"as\" direction=\"out\"/>\n" \
" </method>\n" \
" <method name=\"SetUnitControlGroupAttribute\">\n" \
" <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"attribute\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"values\" type=\"as\" direction=\"in\"/>\n" \
" <arg name=\"mode\" type=\"s\" direction=\"in\"\n/>" \
" </method>\n" \
" <method name=\"UnsetUnitControlGroupAttributes\">\n" \
" <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"attributes\" type=\"a(ss)\" direction=\"in\"/>\n" \
" <arg name=\"attribute\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
" </method>\n" \
" <method name=\"SetUnitControlGroups\">\n" \
" <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"groups\" type=\"as\" direction=\"in\"/>\n" \
" <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
" </method>\n" \
" <method name=\"UnsetUnitControlGroups\">\n" \
" <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"groups\" type=\"as\" direction=\"in\"/>\n" \
" <arg name=\"mode\" type=\"s\" direction=\"in\"\n/>" \
" </method>\n" \
" <method name=\"GetJob\">\n" \
" <arg name=\"id\" type=\"u\" direction=\"in\"/>\n" \
" <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \
@ -874,7 +875,7 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
if (!reply)
goto oom;
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SetUnitControlGroups")) {
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SetUnitControlGroup")) {
const char *name;
Unit *u;
DBusMessageIter iter;
@ -902,7 +903,7 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
if (!reply)
goto oom;
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnsetUnitControlGroups")) {
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnsetUnitControlGroup")) {
const char *name;
Unit *u;
DBusMessageIter iter;
@ -930,7 +931,7 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
if (!reply)
goto oom;
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SetUnitControlGroupAttributes")) {
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SetUnitControlGroupAttribute")) {
const char *name;
Unit *u;
DBusMessageIter iter;
@ -949,6 +950,7 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
}
SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "start");
r = bus_unit_cgroup_attribute_set(u, &iter);
if (r < 0)
return bus_send_error_reply(connection, message, NULL, r);
@ -957,7 +959,7 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
if (!reply)
goto oom;
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnsetUnitControlGroupAttributes")) {
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnsetUnitControlGroupAttribute")) {
const char *name;
Unit *u;
DBusMessageIter iter;
@ -985,7 +987,7 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
if (!reply)
goto oom;
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetUnitControlGroupAttributes")) {
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetUnitControlGroupAttribute")) {
const char *name;
Unit *u;
DBusMessageIter iter;
@ -1005,6 +1007,7 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
}
SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "status");
r = bus_unit_cgroup_attribute_get(u, &iter, &list);
if (r < 0)
return bus_send_error_reply(connection, message, NULL, r);

View File

@ -344,8 +344,8 @@ static int bus_unit_append_cgroup_attrs(DBusMessageIter *i, const char *property
char _cleanup_free_ *v = NULL;
bool success;
if (a->map_callback)
a->map_callback(a->controller, a->name, a->value, &v);
if (a->semantics && a->semantics->map_write)
a->semantics->map_write(a->semantics, a->value, &v);
success =
dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) &&
@ -472,7 +472,7 @@ static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *conn
if (!reply)
goto oom;
} else if (streq_ptr(dbus_message_get_member(message), "SetControlGroups")) {
} else if (streq_ptr(dbus_message_get_member(message), "SetControlGroup")) {
DBusMessageIter iter;
SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "start");
@ -488,7 +488,7 @@ static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *conn
if (!reply)
goto oom;
} else if (streq_ptr(dbus_message_get_member(message), "UnsetControlGroups")) {
} else if (streq_ptr(dbus_message_get_member(message), "UnsetControlGroup")) {
DBusMessageIter iter;
SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
@ -496,14 +496,14 @@ static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *conn
if (!dbus_message_iter_init(message, &iter))
goto oom;
r = bus_unit_cgroup_set(u, &iter);
r = bus_unit_cgroup_unset(u, &iter);
if (r < 0)
return bus_send_error_reply(connection, message, NULL, r);
reply = dbus_message_new_method_return(message);
if (!reply)
goto oom;
} else if (streq_ptr(dbus_message_get_member(message), "GetControlGroupAttributes")) {
} else if (streq_ptr(dbus_message_get_member(message), "GetControlGroupAttribute")) {
DBusMessageIter iter;
_cleanup_strv_free_ char **list = NULL;
@ -524,7 +524,7 @@ static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *conn
if (bus_append_strv_iter(&iter, list) < 0)
goto oom;
} else if (streq_ptr(dbus_message_get_member(message), "SetControlGroupAttributes")) {
} else if (streq_ptr(dbus_message_get_member(message), "SetControlGroupAttribute")) {
DBusMessageIter iter;
SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "start");
@ -540,7 +540,7 @@ static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *conn
if (!reply)
goto oom;
} else if (streq_ptr(dbus_message_get_member(message), "UnsetControlGroupAttributes")) {
} else if (streq_ptr(dbus_message_get_member(message), "UnsetControlGroupAttribute")) {
DBusMessageIter iter;
SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
@ -897,17 +897,17 @@ oom:
return DBUS_HANDLER_RESULT_NEED_MEMORY;
}
static int next_and_parse_mode(DBusMessageIter *iter, bool *runtime) {
static int parse_mode(DBusMessageIter *iter, bool *runtime, bool next) {
const char *mode;
int r;
assert(iter);
assert(runtime);
dbus_message_iter_next(iter);
if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING)
return -EINVAL;
r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &mode, next);
if (r < 0)
return r;
dbus_message_iter_get_basic(iter, &mode);
if (streq(mode, "runtime"))
*runtime = true;
else if (streq(mode, "persistent"))
@ -919,10 +919,11 @@ static int next_and_parse_mode(DBusMessageIter *iter, bool *runtime) {
}
int bus_unit_cgroup_set(Unit *u, DBusMessageIter *iter) {
int r;
_cleanup_strv_free_ char **a = NULL;
char **name;
_cleanup_free_ char *controller = NULL, *old_path = NULL, *new_path = NULL, *contents = NULL;
const char *name;
CGroupBonding *b;
bool runtime;
int r;
assert(u);
assert(iter);
@ -930,111 +931,132 @@ int bus_unit_cgroup_set(Unit *u, DBusMessageIter *iter) {
if (!unit_get_exec_context(u))
return -EINVAL;
r = bus_parse_strv_iter(iter, &a);
r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &name, true);
if (r < 0)
return r;
r = next_and_parse_mode(iter, &runtime);
r = parse_mode(iter, &runtime, false);
if (r < 0)
return r;
STRV_FOREACH(name, a) {
_cleanup_free_ char *controller = NULL, *old_path = NULL, *new_path = NULL, *contents = NULL;
CGroupBonding *b;
r = cg_split_spec(name, &controller, &new_path);
if (r < 0)
return r;
r = cg_split_spec(*name, &controller, &new_path);
if (r < 0)
return r;
b = cgroup_bonding_find_list(u->cgroup_bondings, controller);
if (b) {
old_path = strdup(b->path);
if (!old_path)
return -ENOMEM;
}
r = unit_add_cgroup_from_text(u, *name, true, &b);
if (r < 0)
return r;
if (r > 0) {
/* Try to move things to the new place, and clean up the old place */
cgroup_bonding_realize(b);
cgroup_bonding_migrate(b, u->cgroup_bondings);
if (old_path)
cg_trim(controller, old_path, true);
}
contents = strjoin("[", UNIT_VTABLE(u)->exec_section, "]\n"
"ControlGroup=", *name, "\n", NULL);
if (!contents)
if (!new_path) {
new_path = unit_default_cgroup_path(u);
if (!new_path)
return -ENOMEM;
r = unit_write_drop_in(u, runtime, *name, contents);
if (r < 0)
return r;
}
return 0;
}
int bus_unit_cgroup_unset(Unit *u, DBusMessageIter *iter) {
_cleanup_strv_free_ char **a = NULL;
char **name;
int r;
bool runtime;
assert(u);
assert(iter);
if (!unit_get_exec_context(u))
if (!controller || streq(controller, SYSTEMD_CGROUP_CONTROLLER))
return -EINVAL;
r = bus_parse_strv_iter(iter, &a);
if (r < 0)
return r;
r = next_and_parse_mode(iter, &runtime);
if (r < 0)
return r;
STRV_FOREACH(name, a) {
_cleanup_free_ char *controller = NULL, *path = NULL, *target = NULL;
CGroupBonding *b;
r = cg_split_spec(*name, &controller, &path);
if (r < 0)
return r;
if (!controller || streq(controller, SYSTEMD_CGROUP_CONTROLLER))
return -EINVAL;
unit_remove_drop_in(u, runtime, *name);
b = cgroup_bonding_find_list(u->cgroup_bondings, controller);
if (!b)
continue;
if (path && !path_equal(path, b->path))
continue;
b = cgroup_bonding_find_list(u->cgroup_bondings, controller);
if (b) {
if (streq(b->path, new_path))
return 0;
if (b->essential)
return -EINVAL;
/* Try to migrate the old group away */
if (cg_get_by_pid(controller, 0, &target) >= 0)
cgroup_bonding_migrate_to(u->cgroup_bondings, target, false);
old_path = strdup(b->path);
if (!old_path)
return -ENOMEM;
}
cgroup_bonding_free(b, true);
r = unit_add_cgroup_from_text(u, name, true, &b);
if (r < 0)
return r;
if (r > 0) {
CGroupAttribute *a;
/* Try to move things to the new place, and clean up the old place */
cgroup_bonding_realize(b);
cgroup_bonding_migrate(b, u->cgroup_bondings);
if (old_path)
cg_trim(controller, old_path, true);
/* Apply the attributes to the new group */
LIST_FOREACH(by_unit, a, u->cgroup_attributes)
if (streq(a->controller, controller))
cgroup_attribute_apply(a, b);
}
contents = strjoin("[", UNIT_VTABLE(u)->exec_section, "]\n"
"ControlGroup=", name, "\n", NULL);
if (!contents)
return -ENOMEM;
return unit_write_drop_in(u, runtime, controller, contents);
}
int bus_unit_cgroup_unset(Unit *u, DBusMessageIter *iter) {
_cleanup_free_ char *controller = NULL, *path = NULL, *target = NULL;
const char *name;
CGroupAttribute *a, *n;
CGroupBonding *b;
bool runtime;
int r;
assert(u);
assert(iter);
if (!unit_get_exec_context(u))
return -EINVAL;
r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &name, true);
if (r < 0)
return r;
r = parse_mode(iter, &runtime, false);
if (r < 0)
return r;
r = cg_split_spec(name, &controller, &path);
if (r < 0)
return r;
if (!controller || streq(controller, SYSTEMD_CGROUP_CONTROLLER))
return -EINVAL;
b = cgroup_bonding_find_list(u->cgroup_bondings, controller);
if (!b)
return -ENOENT;
if (path && !path_equal(path, b->path))
return -ENOENT;
if (b->essential)
return -EINVAL;
unit_remove_drop_in(u, runtime, controller);
/* Try to migrate the old group away */
if (cg_get_by_pid(controller, 0, &target) >= 0)
cgroup_bonding_migrate_to(u->cgroup_bondings, target, false);
cgroup_bonding_free(b, true);
/* Drop all attributes of this controller */
LIST_FOREACH_SAFE(by_unit, a, n, u->cgroup_attributes) {
if (!streq(a->controller, controller))
continue;
unit_remove_drop_in(u, runtime, a->name);
cgroup_attribute_free(a);
}
return 0;
}
int bus_unit_cgroup_attribute_get(Unit *u, DBusMessageIter *iter, char ***_result) {
_cleanup_strv_free_ char **l = NULL, **result = NULL;
char **name;
_cleanup_free_ char *controller = NULL;
CGroupAttribute *a;
CGroupBonding *b;
const char *name;
char **l = NULL;
int r;
assert(u);
@ -1044,63 +1066,99 @@ int bus_unit_cgroup_attribute_get(Unit *u, DBusMessageIter *iter, char ***_resul
if (!unit_get_exec_context(u))
return -EINVAL;
r = bus_parse_strv_iter(iter, &l);
r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &name, false);
if (r < 0)
return r;
STRV_FOREACH(name, l) {
_cleanup_free_ char *controller = NULL;
CGroupAttribute *a;
CGroupBonding *b;
r = cg_controller_from_attr(name, &controller);
if (r < 0)
return r;
r = cg_controller_from_attr(*name, &controller);
/* First attempt, read the value from the kernel */
b = cgroup_bonding_find_list(u->cgroup_bondings, controller);
if (b) {
_cleanup_free_ char *p = NULL, *v = NULL;
r = cg_get_path(b->controller, b->path, name, &p);
if (r < 0)
return r;
/* First attempt, read the value from the kernel */
b = cgroup_bonding_find_list(u->cgroup_bondings, controller);
if (b) {
_cleanup_free_ char *p = NULL, *v = NULL;
r = read_full_file(p, &v, NULL);
if (r >= 0) {
/* Split on new lines */
l = strv_split_newlines(v);
if (!l)
return -ENOMEM;
r = cg_get_path(b->controller, b->path, *name, &p);
if (r < 0)
return r;
*_result = l;
return 0;
r = read_full_file(p, &v, NULL);
if (r >= 0) {
r = strv_extend(&result, v);
if (r < 0)
return r;
continue;
} else if (r != -ENOENT)
return r;
}
/* If that didn't work, read our cached value */
a = cgroup_attribute_find_list(u->cgroup_attributes, NULL, *name);
if (a) {
r = strv_extend(&result, a->value);
if (r < 0)
return r;
continue;
}
return -ENOENT;
}
*_result = result;
result = NULL;
/* If that didn't work, read our cached value */
LIST_FOREACH(by_unit, a, u->cgroup_attributes) {
if (!cgroup_attribute_matches(a, controller, name))
continue;
r = strv_extend(&l, a->value);
if (r < 0) {
strv_free(l);
return r;
}
}
if (!l)
return -ENOENT;
*_result = l;
return 0;
}
static int update_attribute_drop_in(Unit *u, bool runtime, const char *name) {
_cleanup_free_ char *buf = NULL;
CGroupAttribute *a;
assert(u);
assert(name);
LIST_FOREACH(by_unit, a, u->cgroup_attributes) {
if (!cgroup_attribute_matches(a, NULL, name))
continue;
if (!buf) {
buf = strjoin("[", UNIT_VTABLE(u)->exec_section, "]\n"
"ControlGroupAttribute=", a->name, " ", a->value, "\n", NULL);
if (!buf)
return -ENOMEM;
} else {
char *b;
b = strjoin(buf,
"ControlGroupAttribute=", a->name, " ", a->value, "\n", NULL);
if (!b)
return -ENOMEM;
free(buf);
buf = b;
}
}
if (buf)
return unit_write_drop_in(u, runtime, name, buf);
else
return unit_remove_drop_in(u, runtime, name);
}
int bus_unit_cgroup_attribute_set(Unit *u, DBusMessageIter *iter) {
_cleanup_strv_free_ char **l = NULL;
int r;
bool runtime = false;
char **name, **value;
char **value;
const char *name;
assert(u);
assert(iter);
@ -1108,19 +1166,34 @@ int bus_unit_cgroup_attribute_set(Unit *u, DBusMessageIter *iter) {
if (!unit_get_exec_context(u))
return -EINVAL;
r = bus_parse_strv_pairs_iter(iter, &l);
r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &name, true);
if (r < 0)
return r;
r = next_and_parse_mode(iter, &runtime);
r = bus_parse_strv_iter(iter, &l);
if (r < 0)
return r;
STRV_FOREACH_PAIR(name, value, l) {
_cleanup_free_ char *contents = NULL;
if (!dbus_message_iter_next(iter))
return -EINVAL;
r = parse_mode(iter, &runtime, false);
if (r < 0)
return r;
STRV_FOREACH(value, l) {
_cleanup_free_ char *v = NULL;
CGroupAttribute *a;
const CGroupSemantics *s;
r = unit_add_cgroup_attribute(u, NULL, *name, *value, NULL, &a);
r = cgroup_semantics_find(NULL, name, *value, &v, &s);
if (r < 0)
return r;
if (s && !s->multiple && l[1])
return -EINVAL;
r = unit_add_cgroup_attribute(u, s, NULL, name, v ? v : *value, &a);
if (r < 0)
return r;
@ -1144,22 +1217,17 @@ int bus_unit_cgroup_attribute_set(Unit *u, DBusMessageIter *iter) {
cgroup_attribute_apply(a, u->cgroup_bondings);
}
contents = strjoin("[", UNIT_VTABLE(u)->exec_section, "]\n"
"ControlGroupAttribute=", *name, " ", *value, "\n", NULL);
if (!contents)
return -ENOMEM;
r = unit_write_drop_in(u, runtime, *name, contents);
if (r < 0)
return r;
}
r = update_attribute_drop_in(u, runtime, name);
if (r < 0)
return r;
return 0;
}
int bus_unit_cgroup_attribute_unset(Unit *u, DBusMessageIter *iter) {
_cleanup_strv_free_ char **l = NULL;
char **name;
const char *name;
bool runtime;
int r;
@ -1169,23 +1237,16 @@ int bus_unit_cgroup_attribute_unset(Unit *u, DBusMessageIter *iter) {
if (!unit_get_exec_context(u))
return -EINVAL;
r = bus_parse_strv_iter(iter, &l);
r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &name, true);
if (r < 0)
return r;
r = next_and_parse_mode(iter, &runtime);
r = parse_mode(iter, &runtime, false);
if (r < 0)
return r;
STRV_FOREACH(name, l) {
CGroupAttribute *a;
a = cgroup_attribute_find_list(u->cgroup_attributes, NULL, *name);
if (a)
cgroup_attribute_free(a);
unit_remove_drop_in(u, runtime, *name);
}
cgroup_attribute_free_some(u->cgroup_attributes, NULL, name);
update_attribute_drop_in(u, runtime, name);
return 0;
}

View File

@ -127,24 +127,25 @@
" <property name=\"DefaultControlGroup\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"ControlGroups\" type=\"as\" access=\"read\"/>\n" \
" <property name=\"ControlGroupAttributes\" type=\"a(sss)\" access=\"read\"/>\n" \
" <method name=\"GetControlGroupAttributes\">\n" \
" <arg name=\"attributes\" type=\"as\" direction=\"in\"/>\n" \
" <method name=\"SetControlGroup\">\n" \
" <arg name=\"group\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
" </method>\n" \
" <method name=\"UnsetControlGroup\">\n" \
" <arg name=\"group\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
" </method>\n" \
" <method name=\"GetControlGroupAttribute\">\n" \
" <arg name=\"attribute\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"values\" type=\"as\" direction=\"out\"/>\n" \
" </method>\n" \
" <method name=\"SetControlGroupAttributes\">\n" \
" <arg name=\"attributes\" type=\"a(ss)\" direction=\"in\"/>\n" \
" <method name=\"SetControlGroupAttribute\">\n" \
" <arg name=\"attribute\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"values\" type=\"as\" direction=\"in\"/>\n" \
" <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
" </method>\n" \
" <method name=\"UnsetControlGroupAttributes\">\n" \
" <arg name=\"attributes\" type=\"as\" direction=\"in\"/>\n" \
" <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
" </method>\n" \
" <method name=\"SetControlGroups\">\n" \
" <arg name=\"groups\" type=\"as\" direction=\"in\"/>\n" \
" <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
" </method>\n" \
" <method name=\"UnsetControlGroups\">\n" \
" <arg name=\"groups\" type=\"as\" direction=\"in\"/>\n" \
" <method name=\"UnsetControlGroupAttribute\">\n" \
" <arg name=\"attribute\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
" </method>\n"

View File

@ -68,14 +68,14 @@ $1.LimitRTPRIO, config_parse_limit, RLIMIT_RTPR
$1.LimitRTTIME, config_parse_limit, RLIMIT_RTTIME, offsetof($1, exec_context.rlimit)
$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.BlockIOWeight, config_parse_unit_blkio_weight, 0, 0
$1.BlockIOReadBandwidth, config_parse_unit_blkio_bandwidth, 0, 0
$1.BlockIOWriteBandwidth, config_parse_unit_blkio_bandwidth, 0, 0
$1.CPUShares, config_parse_unit_cgroup_attr_pretty, 0, 0
$1.MemoryLimit, config_parse_unit_cgroup_attr_pretty, 0, 0
$1.MemorySoftLimit, config_parse_unit_cgroup_attr_pretty, 0, 0
$1.DeviceAllow, config_parse_unit_cgroup_attr_pretty, 0, 0
$1.DeviceDeny, config_parse_unit_cgroup_attr_pretty, 0, 0
$1.BlockIOWeight, config_parse_unit_cgroup_attr_pretty, 0, 0
$1.BlockIOReadBandwidth, config_parse_unit_cgroup_attr_pretty, 0, 0
$1.BlockIOWriteBandwidth, config_parse_unit_cgroup_attr_pretty, 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)

View File

@ -1769,7 +1769,9 @@ int config_parse_unit_cgroup_attr(
void *userdata) {
Unit *u = data;
_cleanup_strv_free_ char **l = NULL;
size_t a, b;
_cleanup_free_ char *n = NULL, *v = NULL;
const CGroupSemantics *s;
int r;
assert(filename);
@ -1784,16 +1786,24 @@ int config_parse_unit_cgroup_attr(
return 0;
}
l = strv_split_quoted(rvalue);
if (!l)
return log_oom();
if (strv_length(l) != 2) {
a = strcspn(rvalue, WHITESPACE);
b = strspn(rvalue + a, WHITESPACE);
if (a <= 0 || b <= 0) {
log_error("[%s:%u] Failed to parse cgroup attribute value, ignoring: %s", filename, line, rvalue);
return 0;
}
r = unit_add_cgroup_attribute(u, NULL, l[0], l[1], NULL, NULL);
n = strndup(rvalue, a);
if (!n)
return log_oom();
r = cgroup_semantics_find(NULL, n, rvalue + a + b, &v, &s);
if (r < 0) {
log_error("[%s:%u] Failed to parse cgroup attribute value, ignoring: %s", filename, line, rvalue);
return 0;
}
r = unit_add_cgroup_attribute(u, s, NULL, n, v ? v : rvalue + a + b, NULL);
if (r < 0) {
log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
return 0;
@ -1802,292 +1812,36 @@ int config_parse_unit_cgroup_attr(
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) {
int config_parse_unit_cgroup_attr_pretty(
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
Unit *u = data;
_cleanup_free_ char *v = NULL;
const CGroupSemantics *s;
int r;
unsigned long ul;
_cleanup_free_ char *t = NULL;
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 log_oom();
r = unit_add_cgroup_attribute(u, "cpu", "cpu.shares", t, NULL, NULL);
r = cgroup_semantics_find(NULL, lvalue, rvalue, &v, &s);
if (r < 0) {
log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
log_error("[%s:%u] Failed to parse cgroup attribute value, ignoring: %s", filename, line, rvalue);
return 0;
} else if (r == 0) {
log_error("[%s:%u] Unknown or unsupported cgroup attribute %s, ignoring: %s", filename, line, lvalue, 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;
_cleanup_free_ char *t = NULL;
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 log_oom();
r = unit_add_cgroup_attribute(u,
"memory",
streq(lvalue, "MemorySoftLimit") ? "memory.soft_limit_in_bytes" : "memory.limit_in_bytes",
t, NULL, NULL);
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) {
_cleanup_strv_free_ char **l = NULL;
assert(controller);
assert(name);
assert(value);
assert(ret);
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)
return -ENOMEM;
} else {
struct stat st;
if (stat(l[0], &st) < 0) {
log_warning("Couldn't stat device %s", l[0]);
return -errno;
}
if (!S_ISCHR(st.st_mode) && !S_ISBLK(st.st_mode)) {
log_warning("%s is not a device.", l[0]);
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)
return -ENOMEM;
}
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;
_cleanup_strv_free_ char **l = NULL;
int r;
unsigned k;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
l = strv_split_quoted(rvalue);
if (!l)
return log_oom();
k = strv_length(l);
if (k < 1 || k > 2) {
log_error("[%s:%u] Failed to parse device value, ignoring: %s", filename, line, rvalue);
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);
return 0;
}
if (!isempty(l[1]) && !in_charset(l[1], "rwm")) {
log_error("[%s:%u] Device access string invalid, ignoring: %s", filename, line, rvalue);
return 0;
}
r = unit_add_cgroup_attribute(u, "devices",
streq(lvalue, "DeviceAllow") ? "devices.allow" : "devices.deny",
rvalue, device_map, NULL);
if (r < 0) {
log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
return 0;
}
return 0;
}
static int blkio_map(const char *controller, const char *name, const char *value, char **ret) {
struct stat st;
_cleanup_strv_free_ char **l = NULL;
dev_t d;
assert(controller);
assert(name);
assert(value);
assert(ret);
l = strv_split_quoted(value);
if (!l)
return log_oom();
assert(strv_length(l) == 2);
if (stat(l[0], &st) < 0) {
log_warning("Couldn't stat device %s", l[0]);
return -errno;
}
if (S_ISBLK(st.st_mode))
d = st.st_rdev;
else if (major(st.st_dev) != 0) {
/* If this is not a device node then find the block
* device this file is stored on */
d = st.st_dev;
/* If this is a partition, try to get the originating
* block device */
block_get_whole_disk(d, &d);
} else {
log_warning("%s is not a block device and file system block device cannot be determined or is not local.", l[0]);
return -ENODEV;
}
if (asprintf(ret, "%u:%u %s", major(d), minor(d), l[1]) < 0)
return -ENOMEM;
return 0;
}
int config_parse_unit_blkio_weight(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;
const char *device = NULL, *weight;
unsigned k;
_cleanup_free_ char *t = NULL;
_cleanup_strv_free_ char **l = NULL;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
l = strv_split_quoted(rvalue);
if (!l)
return log_oom();
k = strv_length(l);
if (k < 1 || k > 2) {
log_error("[%s:%u] Failed to parse weight value, ignoring: %s", filename, line, rvalue);
return 0;
}
if (k == 1)
weight = l[0];
else {
device = l[0];
weight = l[1];
}
if (device && !path_is_absolute(device)) {
log_error("[%s:%u] Failed to parse block device node value, ignoring: %s", filename, line, rvalue);
return 0;
}
if (safe_atolu(weight, &ul) < 0 || ul < 10 || ul > 1000) {
log_error("[%s:%u] Failed to parse block IO weight value, ignoring: %s", filename, line, rvalue);
return 0;
}
if (device)
r = asprintf(&t, "%s %lu", device, ul);
else
r = asprintf(&t, "%lu", ul);
if (r < 0)
return log_oom();
if (device)
r = unit_add_cgroup_attribute(u, "blkio", "blkio.weight_device", t, blkio_map, NULL);
else
r = unit_add_cgroup_attribute(u, "blkio", "blkio.weight", t, NULL, NULL);
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_blkio_bandwidth(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 bytes;
unsigned k;
_cleanup_free_ char *t = NULL;
_cleanup_strv_free_ char **l = NULL;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
l = strv_split_quoted(rvalue);
if (!l)
return log_oom();
k = strv_length(l);
if (k != 2) {
log_error("[%s:%u] Failed to parse bandwidth value, ignoring: %s", filename, line, rvalue);
return 0;
}
if (!path_is_absolute(l[0])) {
log_error("[%s:%u] Failed to parse block device node value, ignoring: %s", filename, line, rvalue);
return 0;
}
if (parse_bytes(l[1], &bytes) < 0 || bytes <= 0) {
log_error("[%s:%u] Failed to parse block IO bandwidth value, ignoring: %s", filename, line, rvalue);
return 0;
}
r = asprintf(&t, "%s %llu", l[0], (unsigned long long) bytes);
if (r < 0)
return log_oom();
r = unit_add_cgroup_attribute(u, "blkio",
streq(lvalue, "BlockIOReadBandwidth") ? "blkio.read_bps_device" : "blkio.write_bps_device",
t, blkio_map, NULL);
r = unit_add_cgroup_attribute(u, s, NULL, NULL, v, NULL);
if (r < 0) {
log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
return 0;

View File

@ -75,11 +75,7 @@ int config_parse_kill_mode(const char *filename, unsigned line, const char *sect
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_start_limit_action(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);
int config_parse_unit_blkio_weight(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_unit_blkio_bandwidth(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_pretty(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_unit_requires_mounts_for(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_syscall_filter(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_environ(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);

View File

@ -749,15 +749,13 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
prefix, b->controller, b->path);
LIST_FOREACH(by_unit, a, u->cgroup_attributes) {
char *v = NULL;
_cleanup_free_ char *v = NULL;
if (a->map_callback)
a->map_callback(a->controller, a->name, a->value, &v);
if (a->semantics && a->semantics->map_write)
a->semantics->map_write(a->semantics, a->value, &v);
fprintf(f, "%s\tControlGroupAttribute: %s %s \"%s\"\n",
prefix, a->controller, a->name, v ? v : a->value);
free(v);
}
if (UNIT_VTABLE(u)->dump)
@ -1900,30 +1898,12 @@ finish:
}
int set_unit_path(const char *p) {
char *cwd, *c;
int r;
_cleanup_free_ char *c = NULL;
/* This is mostly for debug purposes */
if (path_is_absolute(p)) {
if (!(c = strdup(p)))
return -ENOMEM;
} else {
if (!(cwd = get_current_dir_name()))
return -errno;
r = asprintf(&c, "%s/%s", cwd, p);
free(cwd);
if (r < 0)
return -ENOMEM;
}
if (setenv("SYSTEMD_UNIT_PATH", c, 0) < 0) {
r = -errno;
free(c);
return r;
}
c = path_make_absolute_cwd(p);
if (setenv("SYSTEMD_UNIT_PATH", c, 0) < 0)
return -errno;
return 0;
}
@ -2109,7 +2089,7 @@ static int unit_add_one_default_cgroup(Unit *u, const char *controller) {
if (r < 0)
goto fail;
return 0;
return 1;
fail:
free(b->path);
@ -2153,10 +2133,10 @@ CGroupBonding* unit_get_default_cgroup(Unit *u) {
int unit_add_cgroup_attribute(
Unit *u,
const CGroupSemantics *semantics,
const char *controller,
const char *name,
const char *value,
CGroupAttributeMapCallback map_callback,
CGroupAttribute **ret) {
_cleanup_free_ char *c = NULL;
@ -2164,48 +2144,67 @@ int unit_add_cgroup_attribute(
int r;
assert(u);
assert(name);
assert(value);
if (semantics) {
/* Semantics always take precedence */
if (semantics->name)
name = semantics->name;
if (semantics->controller)
controller = semantics->controller;
}
if (!name)
return -EINVAL;
if (!controller) {
r = cg_controller_from_attr(name, &c);
if (r < 0)
return -EINVAL;
controller = c;
} else {
if (!filename_is_safe(name))
return -EINVAL;
if (!filename_is_safe(controller))
return -EINVAL;
}
if (!controller || streq(controller, SYSTEMD_CGROUP_CONTROLLER))
return -EINVAL;
if (!filename_is_safe(name))
return -EINVAL;
if (!filename_is_safe(controller))
return -EINVAL;
/* Check if this attribute already exists. Note that we will
* explicitly check for the value here too, as there are
* attributes which accept multiple values. */
a = cgroup_attribute_find_list(u->cgroup_attributes, controller, name);
if (a) {
char *v;
if (streq(value, a->value)) {
/* Exactly the same value is always OK, let's ignore this */
if (ret)
*ret = a;
return 0;
}
v = strdup(value);
if (!v)
return -ENOMEM;
if (semantics && !semantics->multiple) {
char *v;
free(a->value);
a->value = v;
/* If this is a single-item entry, we can
* simply patch the existing attribute */
if (ret)
*ret = a;
v = strdup(value);
if (!v)
return -ENOMEM;
return 1;
free(a->value);
a->value = v;
if (ret)
*ret = a;
return 1;
}
}
a = new0(CGroupAttribute, 1);
@ -2226,11 +2225,10 @@ int unit_add_cgroup_attribute(
free(a->name);
free(a->value);
free(a);
return -ENOMEM;
}
a->map_callback = map_callback;
a->semantics = semantics;
a->unit = u;
LIST_PREPEND(CGroupAttribute, by_unit, u->cgroup_attributes, a);
@ -2763,23 +2761,58 @@ ExecContext *unit_get_exec_context(Unit *u) {
return (ExecContext*) ((uint8_t*) u + offset);
}
int unit_write_drop_in(Unit *u, bool runtime, const char *name, const char *data) {
_cleanup_free_ char *p = NULL, *q = NULL;
assert(u);
static int drop_in_file(Unit *u, bool runtime, const char *name, char **_p, char **_q) {
char *p, *q;
int r;
if (u->manager->running_as != SYSTEMD_SYSTEM)
assert(u);
assert(name);
assert(_p);
assert(_q);
if (u->manager->running_as == SYSTEMD_USER && runtime)
return -ENOTSUP;
if (!filename_is_safe(name))
return -EINVAL;
p = strjoin(runtime ? "/run/systemd/system/" : "/etc/systemd/system/", u->id, ".d", NULL);
if (u->manager->running_as == SYSTEMD_USER) {
_cleanup_free_ char *c = NULL;
r = user_config_home(&c);
if (r < 0)
return r;
if (r == 0)
return -ENOENT;
p = strjoin(c, "/", u->id, ".d", NULL);
} else if (runtime)
p = strjoin("/run/systemd/system/", u->id, ".d", NULL);
else
p = strjoin("/etc/systemd/system/", u->id, ".d", NULL);
if (!p)
return -ENOMEM;
q = strjoin(p, "/50-", name, ".conf", NULL);
if (!q)
if (!q) {
free(p);
return -ENOMEM;
}
*_p = p;
*_q = q;
return 0;
}
int unit_write_drop_in(Unit *u, bool runtime, const char *name, const char *data) {
_cleanup_free_ char *p = NULL, *q = NULL;
int r;
assert(u);
r = drop_in_file(u, runtime, name, &p, &q);
if (r < 0)
return r;
mkdir_p(p, 0755);
return write_one_line_file_atomic_label(q, data);
@ -2787,28 +2820,18 @@ int unit_write_drop_in(Unit *u, bool runtime, const char *name, const char *data
int unit_remove_drop_in(Unit *u, bool runtime, const char *name) {
_cleanup_free_ char *p = NULL, *q = NULL;
int r;
assert(u);
if (u->manager->running_as != SYSTEMD_SYSTEM)
return -ENOTSUP;
if (!filename_is_safe(name))
return -EINVAL;
p = strjoin(runtime ? "/run/systemd/system/" : "/etc/systemd/system/", u->id, ".d", NULL);
if (!p)
return -ENOMEM;
q = strjoin(p, "/50-", name, ".conf", NULL);
if (!q)
return -ENOMEM;
r = drop_in_file(u, runtime, name, &p, &q);
if (unlink(q) < 0)
return -errno;
r = -errno;
else
r = 0;
rmdir(p);
return 0;
return r;
}
int unit_kill_context(

View File

@ -40,6 +40,7 @@ typedef struct UnitStatusMessageFormats UnitStatusMessageFormats;
#include "condition.h"
#include "install.h"
#include "unit-name.h"
#include "cgroup-semantics.h"
enum UnitActiveState {
UNIT_ACTIVE,
@ -445,7 +446,7 @@ int unit_add_cgroup(Unit *u, CGroupBonding *b);
int unit_add_cgroup_from_text(Unit *u, const char *name, bool overwrite, CGroupBonding **ret);
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, CGroupAttribute **ret);
int unit_add_cgroup_attribute(Unit *u, const CGroupSemantics *semantics, const char *controller, const char *name, const char *value, CGroupAttribute **ret);
int unit_choose_id(Unit *u, const char *name);
int unit_set_description(Unit *u, const char *description);

View File

@ -41,21 +41,26 @@ DEFINE_STRING_TABLE_LOOKUP(systemd_running_as, SystemdRunningAs);
int user_config_home(char **config_home) {
const char *e;
char *r;
e = getenv("XDG_CONFIG_HOME");
if (e) {
if (asprintf(config_home, "%s/systemd/user", e) < 0)
r = strappend(e, "/systemd/user");
if (!r)
return -ENOMEM;
*config_home = r;
return 1;
} else {
const char *home;
home = getenv("HOME");
if (home) {
if (asprintf(config_home, "%s/.config/systemd/user", home) < 0)
r = strappend(home, "/.config/systemd/user");
if (!r)
return -ENOMEM;
*config_home = r;
return 1;
}
}

View File

@ -305,6 +305,31 @@ char **strv_split_quoted(const char *s) {
return r;
}
char **strv_split_newlines(const char *s) {
char **l;
unsigned n;
assert(s);
/* Special version of strv_split() that splits on newlines and
* suppresses an empty string at the end */
l = strv_split(s, NEWLINE);
if (!l)
return NULL;
n = strv_length(l);
if (n <= 0)
return l;
if (isempty(l[n-1])) {
free(l[n-1]);
l[n-1] = NULL;
}
return l;
}
char *strv_join(char **l, const char *separator) {
char *r, *e;
char **s;

View File

@ -58,6 +58,7 @@ static inline bool strv_isempty(char **l) {
char **strv_split(const char *s, const char *separator) _malloc_;
char **strv_split_quoted(const char *s) _malloc_;
char **strv_split_newlines(const char *s) _malloc_;
char *strv_join(char **l, const char *separator) _malloc_;

View File

@ -1963,53 +1963,41 @@ static int kill_unit(DBusConnection *bus, char **args) {
}
static int set_cgroup(DBusConnection *bus, char **args) {
_cleanup_dbus_message_unref_ DBusMessage *m = NULL, *reply = NULL;
DBusError error;
const char *method;
DBusMessageIter iter;
int r;
_cleanup_free_ char *n = NULL;
const char *runtime;
const char *method, *runtime;
char **argument;
int r;
assert(bus);
assert(args);
dbus_error_init(&error);
method =
streq(args[0], "set-cgroup") ? "SetUnitControlGroups" :
streq(args[0], "unset-group") ? "UnsetUnitControlGroups"
: "UnsetUnitControlGroupAttributes";
streq(args[0], "set-cgroup") ? "SetUnitControlGroup" :
streq(args[0], "unset-cgroup") ? "UnsetUnitControlGroup"
: "UnsetUnitControlGroupAttribute";
runtime = arg_runtime ? "runtime" : "persistent";
n = unit_name_mangle(args[1]);
if (!n)
return log_oom();
m = dbus_message_new_method_call(
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
method);
if (!m)
return log_oom();
STRV_FOREACH(argument, args + 2) {
dbus_message_iter_init_append(m, &iter);
if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &n))
return log_oom();
r = bus_append_strv_iter(&iter, args + 2);
if (r < 0)
return log_oom();
runtime = arg_runtime ? "runtime" : "persistent";
if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &runtime))
return log_oom();
reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
if (!reply) {
log_error("Failed to issue method call: %s", bus_error_message(&error));
dbus_error_free(&error);
return -EIO;
r = bus_method_call_with_reply(
bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
method,
NULL,
NULL,
DBUS_TYPE_STRING, &n,
DBUS_TYPE_STRING, argument,
DBUS_TYPE_STRING, &runtime,
DBUS_TYPE_INVALID);
if (r < 0)
return r;
}
return 0;
@ -2018,20 +2006,17 @@ static int set_cgroup(DBusConnection *bus, char **args) {
static int set_cgroup_attr(DBusConnection *bus, char **args) {
_cleanup_dbus_message_unref_ DBusMessage *m = NULL, *reply = NULL;
DBusError error;
DBusMessageIter iter, sub, sub2;
char **x, **y;
DBusMessageIter iter;
_cleanup_free_ char *n = NULL;
const char *runtime;
int r;
assert(bus);
assert(args);
dbus_error_init(&error);
if (strv_length(args) % 2 != 0) {
log_error("Expecting an uneven number of arguments!");
return -EINVAL;
}
runtime = arg_runtime ? "runtime" : "persistent";
n = unit_name_mangle(args[1]);
if (!n)
@ -2041,26 +2026,20 @@ static int set_cgroup_attr(DBusConnection *bus, char **args) {
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"SetUnitControlGroupAttributes");
"SetUnitControlGroupAttribute");
if (!m)
return log_oom();
dbus_message_iter_init_append(m, &iter);
if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &n) ||
!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(ss)", &sub))
!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &args[2]))
return log_oom();
STRV_FOREACH_PAIR(x, y, args + 2) {
if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, x) ||
!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, y) ||
!dbus_message_iter_close_container(&sub, &sub2))
return log_oom();
}
r = bus_append_strv_iter(&iter, args + 3);
if (r < 0)
return log_oom();
runtime = arg_runtime ? "runtime" : "persistent";
if (!dbus_message_iter_close_container(&iter, &sub) ||
!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &runtime))
if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &runtime))
return log_oom();
reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
@ -2075,57 +2054,49 @@ static int set_cgroup_attr(DBusConnection *bus, char **args) {
static int get_cgroup_attr(DBusConnection *bus, char **args) {
_cleanup_dbus_message_unref_ DBusMessage *m = NULL, *reply = NULL;
DBusError error;
DBusMessageIter iter;
int r;
_cleanup_free_ char *n = NULL;
_cleanup_strv_free_ char **list = NULL;
char **a;
char **argument;
int r;
assert(bus);
assert(args);
dbus_error_init(&error);
n = unit_name_mangle(args[1]);
if (!n)
return log_oom();
m = dbus_message_new_method_call(
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"GetUnitControlGroupAttributes");
if (!m)
return log_oom();
STRV_FOREACH(argument, args + 2) {
_cleanup_strv_free_ char **list = NULL;
DBusMessageIter iter;
char **a;
dbus_message_iter_init_append(m, &iter);
if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &n))
return log_oom();
r = bus_method_call_with_reply(
bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"GetUnitControlGroupAttribute",
&reply,
NULL,
DBUS_TYPE_STRING, &n,
DBUS_TYPE_STRING, argument,
DBUS_TYPE_INVALID);
if (r < 0)
return r;
r = bus_append_strv_iter(&iter, args + 2);
if (r < 0)
return log_oom();
dbus_message_iter_init(reply, &iter);
r = bus_parse_strv_iter(&iter, &list);
if (r < 0) {
log_error("Failed to parse value list.");
return r;
}
reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
if (!reply) {
log_error("Failed to issue method call: %s", bus_error_message(&error));
dbus_error_free(&error);
return -EIO;
}
dbus_message_iter_init(reply, &iter);
r = bus_parse_strv_iter(&iter, &list);
if (r < 0) {
log_error("Failed to parse value list.");
return r;
}
STRV_FOREACH(a, list) {
if (endswith(*a, "\n"))
fputs(*a, stdout);
else
puts(*a);
STRV_FOREACH(a, list) {
if (endswith(*a, "\n"))
fputs(*a, stdout);
else
puts(*a);
}
}
return 0;