From: Lennart Poettering Date: Sat, 19 Jan 2013 00:01:41 +0000 (+0100) Subject: unit: optionally allow making cgroup attribute changes persistent X-Git-Tag: v198~459 X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=commitdiff_plain;ds=sidebyside;h=71645acac27da55d510f2e4d61cc61b4e5b93035;p=elogind.git unit: optionally allow making cgroup attribute changes persistent --- diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index d2f8ebf76..d1de46afd 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -896,10 +896,32 @@ oom: return DBUS_HANDLER_RESULT_NEED_MEMORY; } +static int next_and_parse_mode(DBusMessageIter *iter, bool *runtime) { + const char *mode; + + assert(iter); + assert(runtime); + + dbus_message_iter_next(iter); + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) + return -EINVAL; + + dbus_message_iter_get_basic(iter, &mode); + if (streq(mode, "runtime")) + *runtime = true; + else if (streq(mode, "persistent")) + *runtime = false; + else + return -EINVAL; + + return 0; +} + int bus_unit_cgroup_set(Unit *u, DBusMessageIter *iter) { int r; _cleanup_strv_free_ char **a = NULL; char **name; + bool runtime; assert(u); assert(iter); @@ -911,8 +933,12 @@ int bus_unit_cgroup_set(Unit *u, DBusMessageIter *iter) { 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, *old_path = NULL, *new_path = NULL; + _cleanup_free_ char *controller = NULL, *old_path = NULL, *new_path = NULL, *contents = NULL; CGroupBonding *b; r = cg_split_spec(*name, &controller, &new_path); @@ -938,6 +964,15 @@ int bus_unit_cgroup_set(Unit *u, DBusMessageIter *iter) { if (old_path) cg_trim(controller, old_path, true); } + + contents = strjoin("[", UNIT_VTABLE(u)->exec_section, "]\n" + "ControlGroup=", *name, "\n", NULL); + if (!contents) + return -ENOMEM; + + r = unit_write_drop_in(u, runtime, *name, contents); + if (r < 0) + return r; } return 0; @@ -947,6 +982,7 @@ 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); @@ -958,6 +994,10 @@ int bus_unit_cgroup_unset(Unit *u, DBusMessageIter *iter) { 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; @@ -966,6 +1006,11 @@ int bus_unit_cgroup_unset(Unit *u, DBusMessageIter *iter) { 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; @@ -1004,16 +1049,12 @@ int bus_unit_cgroup_attribute_get(Unit *u, DBusMessageIter *iter, char ***_resul STRV_FOREACH(name, l) { _cleanup_free_ char *controller = NULL; - const char *dot; CGroupAttribute *a; CGroupBonding *b; - dot = strchr(*name, '.'); - if (dot) { - controller = strndup(*name, dot - *name); - if (!controller) - return -ENOMEM; - } + 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); @@ -1055,8 +1096,10 @@ int bus_unit_cgroup_attribute_get(Unit *u, DBusMessageIter *iter, char ***_resul } int bus_unit_cgroup_attribute_set(Unit *u, DBusMessageIter *iter) { - DBusMessageIter sub, sub2; + _cleanup_strv_free_ char **l = NULL; int r; + bool runtime = false; + char **name, **value; assert(u); assert(iter); @@ -1064,27 +1107,19 @@ int bus_unit_cgroup_attribute_set(Unit *u, DBusMessageIter *iter) { if (!unit_get_exec_context(u)) return -EINVAL; - if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY || - dbus_message_iter_get_element_type(iter) != DBUS_TYPE_STRUCT) - return -EINVAL; + r = bus_parse_strv_pairs_iter(iter, &l); + if (r < 0) + return r; - dbus_message_iter_recurse(iter, &sub); + r = next_and_parse_mode(iter, &runtime); + if (r < 0) + return r; - while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { - const char *name, *value; + STRV_FOREACH_PAIR(name, value, l) { + _cleanup_free_ char *contents = NULL; CGroupAttribute *a; - assert_se(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT); - - dbus_message_iter_recurse(&sub, &sub2); - - if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0 || - bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &value, false) < 0) - return -EINVAL; - - dbus_message_iter_next(&sub); - - r = unit_add_cgroup_attribute(u, NULL, name, value, NULL, &a); + r = unit_add_cgroup_attribute(u, NULL, *name, *value, NULL, &a); if (r < 0) return r; @@ -1107,6 +1142,15 @@ int bus_unit_cgroup_attribute_set(Unit *u, DBusMessageIter *iter) { /* Make it count */ 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; } return 0; @@ -1115,6 +1159,7 @@ int bus_unit_cgroup_attribute_set(Unit *u, DBusMessageIter *iter) { int bus_unit_cgroup_attribute_unset(Unit *u, DBusMessageIter *iter) { _cleanup_strv_free_ char **l = NULL; char **name; + bool runtime; int r; assert(u); @@ -1127,12 +1172,18 @@ int bus_unit_cgroup_attribute_unset(Unit *u, DBusMessageIter *iter) { if (r < 0) return r; + r = next_and_parse_mode(iter, &runtime); + 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); } return 0; diff --git a/src/core/mount.c b/src/core/mount.c index 18ce73b43..03eff9d9e 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -1888,13 +1888,15 @@ DEFINE_STRING_TABLE_LOOKUP(mount_result, MountResult); const UnitVTable mount_vtable = { .object_size = sizeof(Mount), - .exec_context_offset = offsetof(Mount, exec_context), .sections = "Unit\0" "Mount\0" "Install\0", + .exec_context_offset = offsetof(Mount, exec_context), + .exec_section = "Mount", + .no_alias = true, .no_instances = true, diff --git a/src/core/service.c b/src/core/service.c index bc41617bf..ee5a1a41e 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -3832,13 +3832,15 @@ DEFINE_STRING_TABLE_LOOKUP(start_limit_action, StartLimitAction); const UnitVTable service_vtable = { .object_size = sizeof(Service), - .exec_context_offset = offsetof(Service, exec_context), .sections = "Unit\0" "Service\0" "Install\0", + .exec_context_offset = offsetof(Service, exec_context), + .exec_section = "Service", + .init = service_init, .done = service_done, .load = service_load, diff --git a/src/core/socket.c b/src/core/socket.c index f2d854851..f74d88e60 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -2388,13 +2388,15 @@ DEFINE_STRING_TABLE_LOOKUP(socket_result, SocketResult); const UnitVTable socket_vtable = { .object_size = sizeof(Socket), - .exec_context_offset = offsetof(Socket, exec_context), .sections = "Unit\0" "Socket\0" "Install\0", + .exec_context_offset = offsetof(Socket, exec_context), + .exec_section = "Socket", + .init = socket_init, .done = socket_done, .load = socket_load, diff --git a/src/core/swap.c b/src/core/swap.c index c8e25d066..a2f11875a 100644 --- a/src/core/swap.c +++ b/src/core/swap.c @@ -1382,13 +1382,15 @@ DEFINE_STRING_TABLE_LOOKUP(swap_result, SwapResult); const UnitVTable swap_vtable = { .object_size = sizeof(Swap), - .exec_context_offset = offsetof(Swap, exec_context), .sections = "Unit\0" "Swap\0" "Install\0", + .exec_context_offset = offsetof(Swap, exec_context), + .exec_section = "Swap", + .no_alias = true, .no_instances = true, diff --git a/src/core/unit.c b/src/core/unit.c index 6cf02365e..98237c814 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -45,6 +45,7 @@ #include "cgroup-util.h" #include "missing.h" #include "cgroup-attr.h" +#include "mkdir.h" const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX] = { [UNIT_SERVICE] = &service_vtable, @@ -2758,6 +2759,54 @@ 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); + + if (u->manager->running_as != SYSTEMD_SYSTEM) + return -ENOTSUP; + + if (!filename_is_safe(name)) + return -EINVAL; + + p = strjoin(runtime ? "/run/systemd/system/" : "/etc/systemd/systemd/", u->id, ".d", NULL); + if (!p) + return -ENOMEM; + + q = strjoin(p, "/50-", name, ".conf", NULL); + if (!q) + return -ENOMEM; + + mkdir_p(p, 0755); + return write_one_line_file_atomic(q, data); +} + +int unit_remove_drop_in(Unit *u, bool runtime, const char *name) { + _cleanup_free_ char *p = NULL, *q = NULL; + + 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/systemd/", u->id, ".d", NULL); + if (!p) + return -ENOMEM; + + q = strjoin(p, "/50-", name, ".conf", NULL); + if (!q) + return -ENOMEM; + + if (unlink(q) < 0) + return -errno; + + rmdir(p); + return 0; +} + static const char* const unit_active_state_table[_UNIT_ACTIVE_STATE_MAX] = { [UNIT_ACTIVE] = "active", [UNIT_RELOADING] = "reloading", diff --git a/src/core/unit.h b/src/core/unit.h index 23cd9ef45..297b35d8f 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -271,6 +271,9 @@ struct UnitVTable { * ExecContext is found, if the unit type has that */ size_t exec_context_offset; + /* The name of the section with the exec settings of ExecContext */ + const char *exec_section; + /* Config file sections this unit type understands, separated * by NUL chars */ const char *sections; @@ -551,6 +554,9 @@ int unit_exec_context_defaults(Unit *u, ExecContext *c); ExecContext *unit_get_exec_context(Unit *u); +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); + const char *unit_active_state_to_string(UnitActiveState i); UnitActiveState unit_active_state_from_string(const char *s); diff --git a/src/shared/dbus-common.c b/src/shared/dbus-common.c index e9a78c299..50891a8ad 100644 --- a/src/shared/dbus-common.c +++ b/src/shared/dbus-common.c @@ -927,6 +927,67 @@ int bus_parse_strv_iter(DBusMessageIter *iter, char ***_l) { return 0; } +int bus_parse_strv_pairs_iter(DBusMessageIter *iter, char ***_l) { + DBusMessageIter sub, sub2; + unsigned n = 0, i = 0; + char **l; + + assert(iter); + assert(_l); + + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY || + dbus_message_iter_get_element_type(iter) != DBUS_TYPE_STRUCT) + return -EINVAL; + + dbus_message_iter_recurse(iter, &sub); + + while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { + n++; + dbus_message_iter_next(&sub); + } + + l = new(char*, n*2+1); + if (!l) + return -ENOMEM; + + dbus_message_iter_recurse(iter, &sub); + + while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { + const char *a, *b; + + assert_se(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT); + + dbus_message_iter_recurse(&sub, &sub2); + + if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &a, true) < 0 || + bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &b, false) < 0) + return -EINVAL; + + l[i] = strdup(a); + if (!l[i]) { + strv_free(l); + return -ENOMEM; + } + + l[++i] = strdup(b); + if (!l[i]) { + strv_free(l); + return -ENOMEM; + } + + i++; + dbus_message_iter_next(&sub); + } + + assert(i == n*2); + l[i] = NULL; + + if (_l) + *_l = l; + + return 0; +} + int bus_append_strv_iter(DBusMessageIter *iter, char **l) { DBusMessageIter sub; diff --git a/src/shared/dbus-common.h b/src/shared/dbus-common.h index bcbf18ffa..e141c9ec9 100644 --- a/src/shared/dbus-common.h +++ b/src/shared/dbus-common.h @@ -197,6 +197,7 @@ unsigned bus_events_to_flags(uint32_t events); int bus_parse_strv(DBusMessage *m, char ***_l); int bus_parse_strv_iter(DBusMessageIter *iter, char ***_l); +int bus_parse_strv_pairs_iter(DBusMessageIter *iter, char ***_l); int bus_append_strv_iter(DBusMessageIter *iter, char **l); diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index fb4ae24ed..408a4f050 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -2177,6 +2177,7 @@ static int set_cgroup(DBusConnection *bus, char **args) { DBusMessageIter iter; int r; _cleanup_free_ char *n = NULL; + const char *runtime; assert(bus); assert(args); @@ -2208,6 +2209,10 @@ static int set_cgroup(DBusConnection *bus, char **args) { 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)); @@ -2224,6 +2229,7 @@ static int set_cgroup_attr(DBusConnection *bus, char **args) { DBusMessageIter iter, sub, sub2; char **x, **y; _cleanup_free_ char *n = NULL; + const char *runtime; assert(bus); assert(args); @@ -2260,8 +2266,10 @@ static int set_cgroup_attr(DBusConnection *bus, char **args) { return log_oom(); } - if (!dbus_message_iter_close_container(&iter, &sub)) - return -ENOMEM; + runtime = arg_runtime ? "runtime" : "persistent"; + if (!dbus_message_iter_close_container(&iter, &sub) || + !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) { @@ -3163,7 +3171,7 @@ static int print_property(const char *name, DBusMessageIter *iter) { bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &attr, true) >= 0 && bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &value, false) >= 0) { - printf("ControlGroupAttribute={ controller=%s ; attribute=%s ; value=\"%s\" }\n", + printf("ControlGroupAttributes={ controller=%s ; attribute=%s ; value=\"%s\" }\n", controller, attr, value);