diff --git a/TODO b/TODO index a61fa92b68..7098833881 100644 --- a/TODO +++ b/TODO @@ -28,6 +28,10 @@ Fedora 19: Features: +* implement system-wide DefaultCPUAccounting=1 switch (and similar for blockio, memory, fair scheduling?) + +* handle jointly mounted controllers correctly + * split out CreateMachine into systemd-machined * "transient" units, i.e units that are not sourced from disk but diff --git a/src/core/dbus-cgroup.c b/src/core/dbus-cgroup.c index 08ee9c8db4..f7d1dd12ad 100644 --- a/src/core/dbus-cgroup.c +++ b/src/core/dbus-cgroup.c @@ -137,3 +137,65 @@ const BusProperty bus_cgroup_context_properties[] = { { "DeviceAllow", bus_cgroup_append_device_allow, "a(ss)", 0 }, {} }; + +int bus_cgroup_set_property( + Unit *u, + CGroupContext *c, + const char *name, + DBusMessageIter *i, + UnitSetPropertiesMode mode, + DBusError *error) { + + assert(name); + assert(u); + assert(c); + assert(i); + + if (streq(name, "CPUAccounting")) { + dbus_bool_t b; + + if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_BOOLEAN) + return -EINVAL; + + if (mode != UNIT_CHECK) { + dbus_message_iter_get_basic(i, &b); + + c->cpu_accounting = b; + unit_write_drop_in(u, mode, "cpu-accounting", b ? "CPUAccounting=yes" : "CPUAccounting=no"); + } + + return 1; + + } else if (streq(name, "BlockIOAccounting")) { + dbus_bool_t b; + + if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_BOOLEAN) + return -EINVAL; + + if (mode != UNIT_CHECK) { + dbus_message_iter_get_basic(i, &b); + + c->blockio_accounting = b; + unit_write_drop_in(u, mode, "block-io-accounting", b ? "BlockIOAccounting=yes" : "BlockIOAccounting=no"); + } + + return 1; + } else if (streq(name, "MemoryAccounting")) { + dbus_bool_t b; + + if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_BOOLEAN) + return -EINVAL; + + if (mode != UNIT_CHECK) { + dbus_message_iter_get_basic(i, &b); + + c->blockio_accounting = b; + unit_write_drop_in(u, mode, "memory-accounting", b ? "MemoryAccounting=yes" : "MemoryAccounting=no"); + } + + return 1; + } + + + return 0; +} diff --git a/src/core/dbus-cgroup.h b/src/core/dbus-cgroup.h index a0a7a7771e..c5908dd976 100644 --- a/src/core/dbus-cgroup.h +++ b/src/core/dbus-cgroup.h @@ -42,3 +42,5 @@ " \n" extern const BusProperty bus_cgroup_context_properties[]; + +int bus_cgroup_set_property(Unit *u, CGroupContext *c, const char *name, DBusMessageIter *i, UnitSetPropertiesMode mode, DBusError *error); diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c index c081ff5d16..353cb22867 100644 --- a/src/core/dbus-manager.c +++ b/src/core/dbus-manager.c @@ -228,12 +228,17 @@ " \n" \ " \n" \ " \n" \ - " \n" \ + " \n" \ " \n" \ " \n" \ " \n" \ - " \n" \ - " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ " \n" #define BUS_MANAGER_INTERFACE_SIGNALS \ @@ -1725,13 +1730,42 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, goto oom; r = unit_file_get_default(scope, NULL, &default_target); - if (r < 0) return bus_send_error_reply(connection, message, NULL, r); if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &default_target, DBUS_TYPE_INVALID)) { goto oom; } + + } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SetUnitProperties")) { + DBusMessageIter iter; + dbus_bool_t runtime; + const char *name; + Unit *u; + + if (!dbus_message_iter_init(message, &iter)) + goto oom; + + if (bus_iter_get_basic_and_next(&iter, DBUS_TYPE_STRING, &name, true) < 0 || + bus_iter_get_basic_and_next(&iter, DBUS_TYPE_BOOLEAN, &runtime, true) < 0) + return bus_send_error_reply(connection, message, NULL, -EINVAL); + + u = manager_get_unit(m, name); + if (!u) { + dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name); + return bus_send_error_reply(connection, message, &error, -ENOENT); + } + + SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "start"); + + r = bus_unit_set_properties(u, &iter, runtime ? UNIT_RUNTIME : UNIT_PERSISTENT, &error); + if (r < 0) + return bus_send_error_reply(connection, message, &error, r); + + reply = dbus_message_new_method_return(message); + if (!reply) + goto oom; + } else { const BusBoundProperties bps[] = { { "org.freedesktop.systemd1.Manager", bus_systemd_properties, systemd_property_string }, diff --git a/src/core/dbus-slice.c b/src/core/dbus-slice.c index 8243305848..db356adf30 100644 --- a/src/core/dbus-slice.c +++ b/src/core/dbus-slice.c @@ -53,10 +53,38 @@ DBusHandlerResult bus_slice_message_handler(Unit *u, DBusConnection *c, DBusMess const BusBoundProperties bps[] = { { "org.freedesktop.systemd1.Unit", bus_unit_properties, u }, { "org.freedesktop.systemd1.Slice", bus_cgroup_context_properties, &s->cgroup_context }, - { NULL, } + {} }; SELINUX_UNIT_ACCESS_CHECK(u, c, message, "status"); return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, bps); } + +int bus_slice_set_property( + Unit *u, + const char *name, + DBusMessageIter *i, + UnitSetPropertiesMode mode, + DBusError *error) { + + Slice *s = SLICE(u); + int r; + + assert(name); + assert(u); + assert(i); + + r = bus_cgroup_set_property(u, &s->cgroup_context, name, i, mode, error); + if (r != 0) + return r; + + return 0; +} + +int bus_slice_commit_properties(Unit *u) { + assert(u); + + unit_realize_cgroup(u); + return 0; +} diff --git a/src/core/dbus-slice.h b/src/core/dbus-slice.h index 7e7e29982d..c5ac473763 100644 --- a/src/core/dbus-slice.h +++ b/src/core/dbus-slice.h @@ -27,4 +27,7 @@ DBusHandlerResult bus_slice_message_handler(Unit *u, DBusConnection *c, DBusMessage *message); +int bus_slice_set_property(Unit *u, const char *name, DBusMessageIter *i, UnitSetPropertiesMode mode, DBusError *error); +int bus_slice_commit_properties(Unit *u); + extern const char bus_slice_interface[]; diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index cbd41342f4..1611da0172 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -403,6 +403,25 @@ static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *conn reply = dbus_message_new_method_return(message); if (!reply) goto oom; + } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "SetProperties")) { + DBusMessageIter iter; + dbus_bool_t runtime; + + if (!dbus_message_iter_init(message, &iter)) + goto oom; + + if (bus_iter_get_basic_and_next(&iter, DBUS_TYPE_BOOLEAN, &runtime, true) < 0) + return bus_send_error_reply(connection, message, NULL, -EINVAL); + + SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "start"); + + r = bus_unit_set_properties(u, &iter, runtime ? UNIT_RUNTIME : UNIT_PERSISTENT, &error); + if (r < 0) + return bus_send_error_reply(connection, message, &error, r); + + reply = dbus_message_new_method_return(message); + if (!reply) + goto oom; } else if (UNIT_VTABLE(u)->bus_message_handler) return UNIT_VTABLE(u)->bus_message_handler(u, connection, message); @@ -745,6 +764,75 @@ oom: return DBUS_HANDLER_RESULT_NEED_MEMORY; } +int bus_unit_set_properties(Unit *u, DBusMessageIter *iter, UnitSetPropertiesMode mode, DBusError *error) { + bool for_real = false; + DBusMessageIter sub; + unsigned n = 0; + int r; + + assert(u); + assert(iter); + + /* We iterate through the array twice. First run we just check + * if all passed data is valid, second run actually applies + * it. This is to implement transaction-like behaviour without + * actually providing full transactions. */ + + 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); + + for (;;) { + DBusMessageIter sub2, sub3; + const char *name; + + if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_INVALID) { + + if (for_real) + break; + + /* Reached EOF. Let's try again, and this time for realz... */ + dbus_message_iter_recurse(iter, &sub); + for_real = true; + continue; + } + + if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) + return -EINVAL; + + dbus_message_iter_recurse(&sub, &sub2); + + if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0 || + dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) + return -EINVAL; + + if (!UNIT_VTABLE(u)->bus_set_property) { + dbus_set_error(error, DBUS_ERROR_PROPERTY_READ_ONLY, "Objects of this type do not support setting properties."); + return -ENOENT; + } + + dbus_message_iter_recurse(&sub2, &sub3); + r = UNIT_VTABLE(u)->bus_set_property(u, name, &sub3, for_real ? mode : UNIT_CHECK, error); + if (r < 0) + return r; + if (r == 0) { + dbus_set_error(error, DBUS_ERROR_PROPERTY_READ_ONLY, "Cannot set property %s, or unknown property.", name); + return -ENOENT; + } + + dbus_message_iter_next(&sub); + + n += for_real; + } + + if (n > 0 && UNIT_VTABLE(u)->bus_commit_properties) + UNIT_VTABLE(u)->bus_commit_properties(u); + + return 0; +} + const BusProperty bus_unit_properties[] = { { "Id", bus_property_append_string, "s", offsetof(Unit, id), true }, { "Names", bus_unit_append_names, "as", 0 }, diff --git a/src/core/dbus-unit.h b/src/core/dbus-unit.h index 1e226ef451..2fd56f25c3 100644 --- a/src/core/dbus-unit.h +++ b/src/core/dbus-unit.h @@ -61,6 +61,10 @@ " \n" \ " \n" \ " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ " \n" \ " \n" \ " \n" \ @@ -135,19 +139,9 @@ extern const BusProperty bus_unit_properties[]; void bus_unit_send_change_signal(Unit *u); void bus_unit_send_removed_signal(Unit *u); -DBusHandlerResult bus_unit_queue_job( - DBusConnection *connection, - DBusMessage *message, - Unit *u, - JobType type, - JobMode mode, - bool reload_if_possible); +DBusHandlerResult bus_unit_queue_job(DBusConnection *connection, DBusMessage *message, Unit *u, JobType type, JobMode mode, bool reload_if_possible); -int bus_unit_cgroup_set(Unit *u, DBusMessageIter *iter); -int bus_unit_cgroup_unset(Unit *u, DBusMessageIter *iter); -int bus_unit_cgroup_attribute_get(Unit *u, DBusMessageIter *iter, char ***_result); -int bus_unit_cgroup_attribute_set(Unit *u, DBusMessageIter *iter); -int bus_unit_cgroup_attribute_unset(Unit *u, DBusMessageIter *iter); +int bus_unit_set_properties(Unit *u, DBusMessageIter *i, UnitSetPropertiesMode mode, DBusError *error); extern const DBusObjectPathVTable bus_unit_vtable; diff --git a/src/core/slice.c b/src/core/slice.c index df2d91e473..557f829088 100644 --- a/src/core/slice.c +++ b/src/core/slice.c @@ -304,6 +304,8 @@ const UnitVTable slice_vtable = { .bus_interface = "org.freedesktop.systemd1.Slice", .bus_message_handler = bus_slice_message_handler, + .bus_set_property = bus_slice_set_property, + .bus_commit_properties = bus_slice_commit_properties, .status_message_formats = { .finished_start_job = { diff --git a/src/core/unit.c b/src/core/unit.c index 0dcf85b5e0..be554dac20 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -2642,7 +2642,7 @@ CGroupContext *unit_get_cgroup_context(Unit *u) { return (CGroupContext*) ((uint8_t*) u + offset); } -static int drop_in_file(Unit *u, bool runtime, const char *name, char **_p, char **_q) { +static int drop_in_file(Unit *u, UnitSetPropertiesMode mode, const char *name, char **_p, char **_q) { char *p, *q; int r; @@ -2650,8 +2650,9 @@ static int drop_in_file(Unit *u, bool runtime, const char *name, char **_p, char assert(name); assert(_p); assert(_q); + assert(mode & (UNIT_PERSISTENT|UNIT_RUNTIME)); - if (u->manager->running_as == SYSTEMD_USER && runtime) + if (u->manager->running_as == SYSTEMD_USER && !(mode & UNIT_PERSISTENT)) return -ENOTSUP; if (!filename_is_safe(name)) @@ -2667,10 +2668,10 @@ static int drop_in_file(Unit *u, bool runtime, const char *name, char **_p, char return -ENOENT; p = strjoin(c, "/", u->id, ".d", NULL); - } else if (runtime) - p = strjoin("/run/systemd/system/", u->id, ".d", NULL); - else + } else if (mode & UNIT_PERSISTENT) p = strjoin("/etc/systemd/system/", u->id, ".d", NULL); + else + p = strjoin("/run/systemd/system/", u->id, ".d", NULL); if (!p) return -ENOMEM; @@ -2685,13 +2686,16 @@ static int drop_in_file(Unit *u, bool runtime, const char *name, char **_p, char return 0; } -int unit_write_drop_in(Unit *u, bool runtime, const char *name, const char *data) { +int unit_write_drop_in(Unit *u, UnitSetPropertiesMode mode, 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 (!(mode & (UNIT_PERSISTENT|UNIT_RUNTIME))) + return 0; + + r = drop_in_file(u, mode, name, &p, &q); if (r < 0) return r; @@ -2699,13 +2703,16 @@ int unit_write_drop_in(Unit *u, bool runtime, const char *name, const char *data return write_string_file_atomic_label(q, data); } -int unit_remove_drop_in(Unit *u, bool runtime, const char *name) { +int unit_remove_drop_in(Unit *u, UnitSetPropertiesMode mode, const char *name) { _cleanup_free_ char *p = NULL, *q = NULL; int r; assert(u); - r = drop_in_file(u, runtime, name, &p, &q); + if (!(mode & (UNIT_PERSISTENT|UNIT_RUNTIME))) + return 0; + + r = drop_in_file(u, mode, name, &p, &q); if (unlink(q) < 0) r = -errno; else diff --git a/src/core/unit.h b/src/core/unit.h index fbcaabe167..c344719b16 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -260,6 +260,12 @@ struct UnitStatusMessageFormats { const char *finished_stop_job[_JOB_RESULT_MAX]; }; +typedef enum UnitSetPropertiesMode { + UNIT_CHECK = 0, + UNIT_RUNTIME = 1, + UNIT_PERSISTENT = 2, +} UnitSetPropertiesMode; + #include "service.h" #include "timer.h" #include "socket.h" @@ -372,6 +378,12 @@ struct UnitVTable { /* Called for each message received on the bus */ DBusHandlerResult (*bus_message_handler)(Unit *u, DBusConnection *c, DBusMessage *message); + /* Called for each property that is being set */ + int (*bus_set_property)(Unit *u, const char *name, DBusMessageIter *i, UnitSetPropertiesMode mode, DBusError *error); + + /* Called after at least one property got changed to apply the necessary change */ + int (*bus_commit_properties)(Unit *u); + /* Return the unit this unit is following */ Unit *(*following)(Unit *u); @@ -577,8 +589,8 @@ int unit_exec_context_defaults(Unit *u, ExecContext *c); ExecContext *unit_get_exec_context(Unit *u) _pure_; CGroupContext *unit_get_cgroup_context(Unit *u) _pure_; -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); +int unit_write_drop_in(Unit *u, UnitSetPropertiesMode mode, const char *name, const char *data); +int unit_remove_drop_in(Unit *u, UnitSetPropertiesMode mode, const char *name); int unit_kill_context(Unit *u, KillContext *c, bool sigkill, pid_t main_pid, pid_t control_pid, bool main_pid_alien); diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index a4f8f2326e..1f81bda7e3 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -3596,6 +3596,109 @@ static int show(DBusConnection *bus, char **args) { return ret; } +static int append_assignment(DBusMessageIter *iter, const char *assignment) { + const char *eq; + char *field; + DBusMessageIter sub; + int r; + + assert(iter); + assert(assignment); + + eq = strchr(assignment, '='); + if (!eq) { + log_error("Not an assignment: %s", assignment); + return -EINVAL; + } + + field = strndupa(assignment, eq - assignment); + eq ++; + + if (!dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &field)) + return log_oom(); + + if (streq(field, "CPUAccounting") || + streq(field, "MemoryAccounting") || + streq(field, "BlockIOAccounting")) { + dbus_bool_t b; + + r = parse_boolean(eq); + if (r < 0) { + log_error("Failed to parse boolean assignment %s.", assignment); + return -EINVAL; + } + + b = r; + if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, "b", &sub) || + !dbus_message_iter_append_basic(&sub, DBUS_TYPE_BOOLEAN, &b)) + return log_oom(); + } else { + log_error("Unknown assignment %s.", assignment); + return -EINVAL; + } + + if (!dbus_message_iter_close_container(iter, &sub)) + return log_oom(); + + return 0; +} + +static int set_property(DBusConnection *bus, char **args) { + + _cleanup_free_ DBusMessage *m = NULL, *reply = NULL; + DBusMessageIter iter, sub; + dbus_bool_t runtime; + DBusError error; + char **i; + int r; + + dbus_error_init(&error); + + m = dbus_message_new_method_call( + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "SetUnitProperties"); + if (!m) + return log_oom(); + + dbus_message_iter_init_append(m, &iter); + + runtime = arg_runtime; + + if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &args[1]) || + !dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &runtime) || + !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(sv)", &sub)) + return log_oom(); + + STRV_FOREACH(i, args + 2) { + DBusMessageIter sub2; + + if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2)) + return log_oom(); + + r = append_assignment(&sub2, *i); + if (r < 0) + return r; + + if (!dbus_message_iter_close_container(&sub, &sub2)) + return log_oom(); + + } + + if (!dbus_message_iter_close_container(&iter, &sub)) + 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; + } + + return 0; +} + static int dump(DBusConnection *bus, char **args) { _cleanup_free_ DBusMessage *reply = NULL; DBusError error; @@ -4560,6 +4663,8 @@ static int systemctl_help(void) { " status [NAME...|PID...] Show runtime status of one or more units\n" " show [NAME...|JOB...] Show properties of one or more\n" " units/jobs or the manager\n" + " set-property [NAME] [ASSIGNMENT...]\n" + " Sets one or more properties of a unit\n" " help [NAME...|PID...] Show manual for one or more units\n" " reset-failed [NAME...] Reset failed state for all, one, or more\n" " units\n" @@ -5639,6 +5744,7 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError { "set-default", EQUAL, 2, enable_unit }, { "get-default", LESS, 1, get_default }, { "set-log-level", EQUAL, 2, set_log_level }, + { "set-property", MORE, 3, set_property }, }; int left;