summaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/cgroup-attr.c58
-rw-r--r--src/core/cgroup-attr.h7
-rw-r--r--src/core/cgroup-semantics.c333
-rw-r--r--src/core/cgroup-semantics.h43
-rw-r--r--src/core/dbus-manager.c41
-rw-r--r--src/core/dbus-unit.c329
-rw-r--r--src/core/dbus-unit.h25
-rw-r--r--src/core/load-fragment-gperf.gperf.m416
-rw-r--r--src/core/load-fragment.c300
-rw-r--r--src/core/load-fragment.h6
-rw-r--r--src/core/unit.c159
-rw-r--r--src/core/unit.h3
12 files changed, 776 insertions, 544 deletions
diff --git a/src/core/cgroup-attr.c b/src/core/cgroup-attr.c
index 1373684bdb..2ab4d4623e 100644
--- a/src/core/cgroup-attr.c
+++ b/src/core/cgroup-attr.c
@@ -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);
+}
diff --git a/src/core/cgroup-attr.h b/src/core/cgroup-attr.h
index 0f5b854898..0b542981e8 100644
--- a/src/core/cgroup-attr.h
+++ b/src/core/cgroup-attr.h
@@ -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);
diff --git a/src/core/cgroup-semantics.c b/src/core/cgroup-semantics.c
new file mode 100644
index 0000000000..82b02bbd78
--- /dev/null
+++ b/src/core/cgroup-semantics.c
@@ -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;
+}
diff --git a/src/core/cgroup-semantics.h b/src/core/cgroup-semantics.h
new file mode 100644
index 0000000000..4f848f4bb7
--- /dev/null
+++ b/src/core/cgroup-semantics.h
@@ -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);
diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c
index de23369397..8f4bbc59b7 100644
--- a/src/core/dbus-manager.c
+++ b/src/core/dbus-manager.c
@@ -103,30 +103,31 @@
" <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=\"UnsetUnitControlGroupAttributes\">\n" \
+ " <method name=\"GetUnitControlGroupAttribute\">\n" \
" <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
- " <arg name=\"attributes\" type=\"a(ss)\" direction=\"in\"/>\n" \
- " <arg name=\"mode\" 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=\"SetUnitControlGroups\">\n" \
+ " <method name=\"SetUnitControlGroupAttribute\">\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" \
+ " <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=\"UnsetUnitControlGroups\">\n" \
+ " <method name=\"UnsetUnitControlGroupAttributes\">\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/>" \
+ " <arg name=\"attribute\" type=\"s\" 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" \
@@ -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);
diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c
index 4f968c20f2..7c23e1e616 100644
--- a/src/core/dbus-unit.c
+++ b/src/core/dbus-unit.c
@@ -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,60 +931,74 @@ 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;
- }
+ if (!new_path) {
+ new_path = unit_default_cgroup_path(u);
+ if (!new_path)
+ return -ENOMEM;
+ }
- r = unit_add_cgroup_from_text(u, *name, true, &b);
- if (r < 0)
- return r;
+ if (!controller || streq(controller, SYSTEMD_CGROUP_CONTROLLER))
+ return -EINVAL;
- 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);
+ b = cgroup_bonding_find_list(u->cgroup_bondings, controller);
+ if (b) {
+ if (streq(b->path, new_path))
+ return 0;
- if (old_path)
- cg_trim(controller, old_path, true);
- }
+ if (b->essential)
+ return -EINVAL;
- contents = strjoin("[", UNIT_VTABLE(u)->exec_section, "]\n"
- "ControlGroup=", *name, "\n", NULL);
- if (!contents)
+ old_path = strdup(b->path);
+ if (!old_path)
return -ENOMEM;
+ }
- r = unit_write_drop_in(u, runtime, *name, contents);
- if (r < 0)
- return r;
+ 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);
}
- return 0;
+ 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_strv_free_ char **a = NULL;
- char **name;
- int r;
+ _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);
@@ -991,50 +1006,57 @@ int bus_unit_cgroup_unset(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, *path = NULL, *target = NULL;
- CGroupBonding *b;
+ r = cg_split_spec(name, &controller, &path);
+ 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;
- if (!controller || streq(controller, SYSTEMD_CGROUP_CONTROLLER))
- return -EINVAL;
+ b = cgroup_bonding_find_list(u->cgroup_bondings, controller);
+ if (!b)
+ return -ENOENT;
- unit_remove_drop_in(u, runtime, *name);
+ if (path && !path_equal(path, b->path))
+ return -ENOENT;
- b = cgroup_bonding_find_list(u->cgroup_bondings, controller);
- if (!b)
- continue;
+ if (b->essential)
+ return -EINVAL;
- if (path && !path_equal(path, b->path))
- continue;
+ unit_remove_drop_in(u, runtime, controller);
- 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);
+
+ cgroup_bonding_free(b, true);
- /* 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);
+ /* Drop all attributes of this controller */
+ LIST_FOREACH_SAFE(by_unit, a, n, u->cgroup_attributes) {
+ if (!streq(a->controller, controller))
+ continue;
- cgroup_bonding_free(b, true);
+ 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;
+
+ /* 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_controller_from_attr(*name, &controller);
+ 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;
+ /* 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;
- }
- return -ENOENT;
+ r = strv_extend(&l, a->value);
+ if (r < 0) {
+ strv_free(l);
+ return r;
+ }
}
- *_result = result;
- result = NULL;
+ 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 = bus_parse_strv_iter(iter, &l);
if (r < 0)
return r;
- r = next_and_parse_mode(iter, &runtime);
+ if (!dbus_message_iter_next(iter))
+ return -EINVAL;
+
+ r = parse_mode(iter, &runtime, false);
if (r < 0)
return r;
- STRV_FOREACH_PAIR(name, value, l) {
- _cleanup_free_ char *contents = NULL;
+ STRV_FOREACH(value, l) {
+ _cleanup_free_ char *v = NULL;
CGroupAttribute *a;
+ const CGroupSemantics *s;
+
+ 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, NULL, *name, *value, NULL, &a);
+ 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;
}
diff --git a/src/core/dbus-unit.h b/src/core/dbus-unit.h
index c8903f8e5e..34980b046e 100644
--- a/src/core/dbus-unit.h
+++ b/src/core/dbus-unit.h
@@ -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" \
- " <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=\"SetControlGroup\">\n" \
+ " <arg name=\"group\" type=\"s\" 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" \
+ " <method name=\"UnsetControlGroup\">\n" \
+ " <arg name=\"group\" type=\"s\" 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" \
+ " <method name=\"GetControlGroupAttribute\">\n" \
+ " <arg name=\"attribute\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"values\" type=\"as\" direction=\"out\"/>\n" \
+ " </method>\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=\"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"
diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4
index 0b6a5cc659..22c1761dc2 100644
--- a/src/core/load-fragment-gperf.gperf.m4
+++ b/src/core/load-fragment-gperf.gperf.m4
@@ -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)
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index 6e333aaf15..d79e1d936c 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -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,160 +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);
- 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;
- _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);
- 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;
- _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)
+ n = strndup(rvalue, a);
+ if (!n)
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);
+ r = cgroup_semantics_find(NULL, n, rvalue + a + b, &v, &s);
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);
+ log_error("[%s:%u] Failed to parse cgroup attribute value, 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);
-
+ 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;
@@ -1946,148 +1812,36 @@ int config_parse_unit_device_allow(const char *filename, unsigned line, const ch
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_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_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;
+ _cleanup_free_ char *v = NULL;
+ const CGroupSemantics *s;
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);
+ 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);
- 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);
+ log_error("[%s:%u] Failed to parse cgroup attribute 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);
+ } else if (r == 0) {
+ log_error("[%s:%u] Unknown or unsupported cgroup attribute %s, ignoring: %s", filename, line, lvalue, 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;
diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h
index 421b4c33ec..dfb2ef0c3b 100644
--- a/src/core/load-fragment.h
+++ b/src/core/load-fragment.h
@@ -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);
diff --git a/src/core/unit.c b/src/core/unit.c
index 3a88996eb7..370ad67e59 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -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 */
+
+ v = strdup(value);
+ if (!v)
+ return -ENOMEM;
- if (ret)
- *ret = a;
+ free(a->value);
+ a->value = v;
- return 1;
+ 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,52 +2761,77 @@ 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;
+static int drop_in_file(Unit *u, bool runtime, const char *name, char **_p, char **_q) {
+ char *p, *q;
+ int r;
+
assert(u);
+ assert(name);
+ assert(_p);
+ assert(_q);
- if (u->manager->running_as != SYSTEMD_SYSTEM)
+ 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;
+ }
- mkdir_p(p, 0755);
- return write_one_line_file_atomic_label(q, data);
+ *_p = p;
+ *_q = q;
+ return 0;
}
-int unit_remove_drop_in(Unit *u, bool runtime, const char *name) {
+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);
- if (u->manager->running_as != SYSTEMD_SYSTEM)
- return -ENOTSUP;
+ r = drop_in_file(u, runtime, name, &p, &q);
+ if (r < 0)
+ return r;
- if (!filename_is_safe(name))
- return -EINVAL;
+ mkdir_p(p, 0755);
+ return write_one_line_file_atomic_label(q, data);
+}
- p = strjoin(runtime ? "/run/systemd/system/" : "/etc/systemd/system/", u->id, ".d", NULL);
- if (!p)
- return -ENOMEM;
+int unit_remove_drop_in(Unit *u, bool runtime, const char *name) {
+ _cleanup_free_ char *p = NULL, *q = NULL;
+ int r;
- q = strjoin(p, "/50-", name, ".conf", NULL);
- if (!q)
- return -ENOMEM;
+ assert(u);
+ 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(
diff --git a/src/core/unit.h b/src/core/unit.h
index c90210302d..17a5a5f0d9 100644
--- a/src/core/unit.h
+++ b/src/core/unit.h
@@ -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);