X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fcore%2Fdbus-unit.c;h=dc7d1f13f03266232dd4578839d08147b92edcff;hp=d86f60e498061e485c0533e16b80d35ccb74fc2e;hb=c6a818c82035da91e;hpb=e2417e4143bb892e4599b01de7b031763421bb64 diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index d86f60e49..dc7d1f13f 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -27,6 +27,10 @@ #include "bus-errors.h" #include "dbus-common.h" #include "selinux-access.h" +#include "cgroup-util.h" +#include "strv.h" +#include "path-util.h" +#include "fileio.h" const char bus_unit_interface[] _introspect_("Unit") = BUS_UNIT_INTERFACE; @@ -235,7 +239,7 @@ static int bus_unit_append_can_isolate(DBusMessageIter *i, const char *property, static int bus_unit_append_job(DBusMessageIter *i, const char *property, void *data) { Unit *u = data; DBusMessageIter sub; - char *p; + _cleanup_free_ char *p = NULL; assert(i); assert(property); @@ -246,14 +250,13 @@ static int bus_unit_append_job(DBusMessageIter *i, const char *property, void *d if (u->job) { - if (!(p = job_dbus_path(u->job))) + p = job_dbus_path(u->job); + if (!p) return -ENOMEM; if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &u->job->id) || - !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p)) { - free(p); + !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p)) return -ENOMEM; - } } else { uint32_t id = 0; @@ -261,18 +264,15 @@ static int bus_unit_append_job(DBusMessageIter *i, const char *property, void *d * data. Since we need to fill in a valid path we * simple point to ourselves. */ - if (!(p = unit_dbus_path(u))) + p = unit_dbus_path(u); + if (!p) return -ENOMEM; if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &id) || - !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p)) { - free(p); + !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p)) return -ENOMEM; - } } - free(p); - if (!dbus_message_iter_close_container(i, &sub)) return -ENOMEM; @@ -289,8 +289,10 @@ static int bus_unit_append_default_cgroup(DBusMessageIter *i, const char *proper assert(property); assert(u); - if ((cgb = unit_get_default_cgroup(u))) { - if (!(t = cgroup_bonding_to_string(cgb))) + cgb = unit_get_default_cgroup(u); + if (cgb) { + t = cgroup_bonding_to_string(cgb); + if (!t) return -ENOMEM; } else t = (char*) ""; @@ -312,15 +314,14 @@ static int bus_unit_append_cgroups(DBusMessageIter *i, const char *property, voi return -ENOMEM; LIST_FOREACH(by_unit, cgb, u->cgroup_bondings) { - char *t; + char _cleanup_free_ *t = NULL; bool success; - if (!(t = cgroup_bonding_to_string(cgb))) + t = cgroup_bonding_to_string(cgb); + if (!t) return -ENOMEM; success = dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &t); - free(t); - if (!success) return -ENOMEM; } @@ -340,11 +341,11 @@ static int bus_unit_append_cgroup_attrs(DBusMessageIter *i, const char *property return -ENOMEM; LIST_FOREACH(by_unit, a, u->cgroup_attributes) { - char *v = NULL; + 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) && @@ -352,9 +353,6 @@ static int bus_unit_append_cgroup_attrs(DBusMessageIter *i, const char *property dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &a->name) && dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, v ? &v : &a->value) && dbus_message_iter_close_container(&sub, &sub2); - - free(v); - if (!success) return -ENOMEM; } @@ -406,25 +404,14 @@ static int bus_unit_append_load_error(DBusMessageIter *i, const char *property, } static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *connection, DBusMessage *message) { - DBusMessage *reply = NULL; - Manager *m = u->manager; + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; DBusError error; JobType job_type = _JOB_TYPE_INVALID; - char *path = NULL; bool reload_if_possible = false; int r; dbus_error_init(&error); - r = selinux_unit_access_check( - connection, - message, - m, - (u->fragment_path ? u->fragment_path: u->source_path), - &error); - if (r) - return bus_send_error_reply(connection, message, &error, r); - if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Start")) job_type = JOB_START; else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Stop")) @@ -465,6 +452,8 @@ static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *conn if (signo <= 0 || signo >= _NSIG) return bus_send_error_reply(connection, message, &error, -EINVAL); + SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop"); + r = unit_kill(u, who, signo, &error); if (r < 0) return bus_send_error_reply(connection, message, &error, r); @@ -475,9 +464,96 @@ static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *conn } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ResetFailed")) { + SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "reload"); + unit_reset_failed(u); - if (!(reply = dbus_message_new_method_return(message))) + reply = dbus_message_new_method_return(message); + if (!reply) + goto oom; + + } else if (streq_ptr(dbus_message_get_member(message), "SetControlGroup")) { + DBusMessageIter iter; + + SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "start"); + + if (!dbus_message_iter_init(message, &iter)) + goto oom; + + r = bus_unit_cgroup_set(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), "UnsetControlGroup")) { + DBusMessageIter iter; + + SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop"); + + if (!dbus_message_iter_init(message, &iter)) + goto oom; + + 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), "GetControlGroupAttribute")) { + DBusMessageIter iter; + _cleanup_strv_free_ char **list = NULL; + + SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "status"); + + if (!dbus_message_iter_init(message, &iter)) + goto oom; + + r = bus_unit_cgroup_attribute_get(u, &iter, &list); + if (r < 0) + return bus_send_error_reply(connection, message, NULL, r); + + reply = dbus_message_new_method_return(message); + if (!reply) + goto oom; + + dbus_message_iter_init_append(reply, &iter); + if (bus_append_strv_iter(&iter, list) < 0) + goto oom; + + } else if (streq_ptr(dbus_message_get_member(message), "SetControlGroupAttribute")) { + DBusMessageIter iter; + + SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "start"); + + if (!dbus_message_iter_init(message, &iter)) + goto oom; + + r = bus_unit_cgroup_attribute_set(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), "UnsetControlGroupAttribute")) { + DBusMessageIter iter; + + SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop"); + + if (!dbus_message_iter_init(message, &iter)) + goto oom; + + r = bus_unit_cgroup_attribute_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 (UNIT_VTABLE(u)->bus_message_handler) @@ -488,15 +564,6 @@ static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *conn if (job_type != _JOB_TYPE_INVALID) { const char *smode; JobMode mode; - Job *j; - - if ((job_type == JOB_START && u->refuse_manual_start) || - (job_type == JOB_STOP && u->refuse_manual_stop) || - ((job_type == JOB_RESTART || job_type == JOB_TRY_RESTART) && - (u->refuse_manual_start || u->refuse_manual_stop))) { - dbus_set_error(&error, BUS_ERROR_ONLY_BY_DEPENDENCY, "Operation refused, may be requested by dependency only."); - return bus_send_error_reply(connection, message, &error, -EPERM); - } if (!dbus_message_get_args( message, @@ -505,53 +572,23 @@ static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *conn DBUS_TYPE_INVALID)) return bus_send_error_reply(connection, message, &error, -EINVAL); - if (reload_if_possible && unit_can_reload(u)) { - if (job_type == JOB_RESTART) - job_type = JOB_RELOAD_OR_START; - else if (job_type == JOB_TRY_RESTART) - job_type = JOB_RELOAD; - } - - if ((mode = job_mode_from_string(smode)) == _JOB_MODE_INVALID) { + mode = job_mode_from_string(smode); + if (mode < 0) { dbus_set_error(&error, BUS_ERROR_INVALID_JOB_MODE, "Job mode %s is invalid.", smode); return bus_send_error_reply(connection, message, &error, -EINVAL); } - if ((r = manager_add_job(m, job_type, u, mode, true, &error, &j)) < 0) - return bus_send_error_reply(connection, message, &error, r); - - if (!(reply = dbus_message_new_method_return(message))) - goto oom; - - if (!(path = job_dbus_path(j))) - goto oom; - - if (!dbus_message_append_args( - reply, - DBUS_TYPE_OBJECT_PATH, &path, - DBUS_TYPE_INVALID)) - goto oom; + return bus_unit_queue_job(connection, message, u, job_type, mode, reload_if_possible); } - if (reply) { - if (!dbus_connection_send(connection, reply, NULL)) + if (reply) + if (!bus_maybe_send_reply(connection, message, reply)) goto oom; - dbus_message_unref(reply); - } - - free(path); - return DBUS_HANDLER_RESULT_HANDLED; oom: - free(path); - - if (reply) - dbus_message_unref(reply); - dbus_error_free(&error); - return DBUS_HANDLER_RESULT_NEED_MEMORY; } @@ -559,7 +596,7 @@ static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DB Manager *m = data; Unit *u; int r; - DBusMessage *reply = NULL; + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; DBusError error; assert(connection); @@ -571,6 +608,8 @@ static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DB if (streq(dbus_message_get_path(message), "/org/freedesktop/systemd1/unit")) { /* Be nice to gdbus and return introspection data for our mid-level paths */ + SELINUX_ACCESS_CHECK(connection, message, "status"); + if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) { char *introspection = NULL; FILE *f; @@ -578,7 +617,8 @@ static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DB const char *k; size_t size; - if (!(reply = dbus_message_new_method_return(message))) + reply = dbus_message_new_method_return(message); + if (!reply) goto oom; /* We roll our own introspection code here, instead of @@ -586,7 +626,8 @@ static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DB * need to generate our introspection string * dynamically. */ - if (!(f = open_memstream(&introspection, &size))) + f = open_memstream(&introspection, &size); + if (!f) goto oom; fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE @@ -601,7 +642,8 @@ static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DB if (k != u->id) continue; - if (!(p = bus_path_escape(k))) { + p = bus_path_escape(k); + if (!p) { fclose(f); free(introspection); goto oom; @@ -631,11 +673,9 @@ static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DB free(introspection); - if (!dbus_connection_send(connection, reply, NULL)) + if (!bus_maybe_send_reply(connection, message, reply)) goto oom; - dbus_message_unref(reply); - return DBUS_HANDLER_RESULT_HANDLED; } @@ -643,19 +683,14 @@ static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DB } r = manager_load_unit_from_dbus_path(m, dbus_message_get_path(message), &error, &u); - if (r < 0) { - if (r == -ENOMEM) - goto oom; - + if (r == -ENOMEM) + goto oom; + if (r < 0) return bus_send_error_reply(connection, message, &error, r); - } return bus_unit_message_dispatch(u, connection, message); oom: - if (reply) - dbus_message_unref(reply); - dbus_error_free(&error); return DBUS_HANDLER_RESULT_NEED_MEMORY; @@ -666,8 +701,8 @@ const DBusObjectPathVTable bus_unit_vtable = { }; void bus_unit_send_change_signal(Unit *u) { - char *p = NULL; - DBusMessage *m = NULL; + _cleanup_free_ char *p = NULL; + _cleanup_dbus_message_unref_ DBusMessage *m = NULL; assert(u); @@ -684,7 +719,8 @@ void bus_unit_send_change_signal(Unit *u) { return; } - if (!(p = unit_dbus_path(u))) + p = unit_dbus_path(u); + if (!p) goto oom; if (u->sent_dbus_new_signal) { @@ -695,9 +731,10 @@ void bus_unit_send_change_signal(Unit *u) { if (UNIT_VTABLE(u)->bus_invalidating_properties) { - if (!(m = bus_properties_changed_new(p, - UNIT_VTABLE(u)->bus_interface, - UNIT_VTABLE(u)->bus_invalidating_properties))) + m = bus_properties_changed_new(p, + UNIT_VTABLE(u)->bus_interface, + UNIT_VTABLE(u)->bus_invalidating_properties); + if (!m) goto oom; if (bus_broadcast(u->manager, m) < 0) @@ -706,13 +743,18 @@ void bus_unit_send_change_signal(Unit *u) { dbus_message_unref(m); } - if (!(m = bus_properties_changed_new(p, "org.freedesktop.systemd1.Unit", INVALIDATING_PROPERTIES))) + m = bus_properties_changed_new(p, "org.freedesktop.systemd1.Unit", + INVALIDATING_PROPERTIES); + if (!m) goto oom; } else { /* Send a new signal */ - if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "UnitNew"))) + m = dbus_message_new_signal("/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "UnitNew"); + if (!m) goto oom; if (!dbus_message_append_args(m, @@ -725,25 +767,17 @@ void bus_unit_send_change_signal(Unit *u) { if (bus_broadcast(u->manager, m) < 0) goto oom; - free(p); - dbus_message_unref(m); - u->sent_dbus_new_signal = true; return; oom: - free(p); - - if (m) - dbus_message_unref(m); - - log_error("Failed to allocate unit change/new signal."); + log_oom(); } void bus_unit_send_removed_signal(Unit *u) { - char *p = NULL; - DBusMessage *m = NULL; + _cleanup_free_ char *p = NULL; + _cleanup_dbus_message_unref_ DBusMessage *m = NULL; assert(u); @@ -756,10 +790,14 @@ void bus_unit_send_removed_signal(Unit *u) { if (!u->id) return; - if (!(p = unit_dbus_path(u))) + p = unit_dbus_path(u); + if (!p) goto oom; - if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "UnitRemoved"))) + m = dbus_message_new_signal("/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "UnitRemoved"); + if (!m) goto oom; if (!dbus_message_append_args(m, @@ -771,18 +809,446 @@ void bus_unit_send_removed_signal(Unit *u) { if (bus_broadcast(u->manager, m) < 0) goto oom; - free(p); - dbus_message_unref(m); - return; oom: - free(p); + log_oom(); +} + +DBusHandlerResult bus_unit_queue_job( + DBusConnection *connection, + DBusMessage *message, + Unit *u, + JobType type, + JobMode mode, + bool reload_if_possible) { + + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; + _cleanup_free_ char *path = NULL; + Job *j; + JobBusClient *cl; + DBusError error; + int r; + + assert(connection); + assert(message); + assert(u); + assert(type >= 0 && type < _JOB_TYPE_MAX); + assert(mode >= 0 && mode < _JOB_MODE_MAX); + + dbus_error_init(&error); + + if (reload_if_possible && unit_can_reload(u)) { + if (type == JOB_RESTART) + type = JOB_RELOAD_OR_START; + else if (type == JOB_TRY_RESTART) + type = JOB_RELOAD; + } + + SELINUX_UNIT_ACCESS_CHECK(u, connection, message, + (type == JOB_START || type == JOB_RESTART || type == JOB_TRY_RESTART) ? "start" : + type == JOB_STOP ? "stop" : "reload"); + + if (type == JOB_STOP && u->load_state == UNIT_ERROR && unit_active_state(u) == UNIT_INACTIVE) { + dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s not loaded.", u->id); + return bus_send_error_reply(connection, message, &error, -EPERM); + } + + if ((type == JOB_START && u->refuse_manual_start) || + (type == JOB_STOP && u->refuse_manual_stop) || + ((type == JOB_RESTART || type == JOB_TRY_RESTART) && (u->refuse_manual_start || u->refuse_manual_stop))) { + dbus_set_error(&error, BUS_ERROR_ONLY_BY_DEPENDENCY, + "Operation refused, unit %s may be requested by dependency only.", u->id); + return bus_send_error_reply(connection, message, &error, -EPERM); + } + + r = manager_add_job(u->manager, type, u, mode, true, &error, &j); + if (r < 0) + return bus_send_error_reply(connection, message, &error, r); + + cl = job_bus_client_new(connection, bus_message_get_sender_with_fallback(message)); + if (!cl) + goto oom; + + LIST_PREPEND(JobBusClient, client, j->bus_client_list, cl); + + reply = dbus_message_new_method_return(message); + if (!reply) + goto oom; + + path = job_dbus_path(j); + if (!path) + goto oom; + + if (!dbus_message_append_args( + reply, + DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_INVALID)) + goto oom; + + if (!bus_maybe_send_reply(connection, message, reply)) + goto oom; + + return DBUS_HANDLER_RESULT_HANDLED; + +oom: + dbus_error_free(&error); + + return DBUS_HANDLER_RESULT_NEED_MEMORY; +} + +static int parse_mode(DBusMessageIter *iter, bool *runtime, bool next) { + const char *mode; + int r; - if (m) - dbus_message_unref(m); + assert(iter); + assert(runtime); - log_error("Failed to allocate unit remove signal."); + r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &mode, next); + if (r < 0) + return r; + + 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) { + _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); + + 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, &new_path); + if (r < 0) + return r; + + if (!new_path) { + new_path = unit_default_cgroup_path(u); + if (!new_path) + return -ENOMEM; + } + + if (!controller || streq(controller, SYSTEMD_CGROUP_CONTROLLER)) + return -EINVAL; + + b = cgroup_bonding_find_list(u->cgroup_bondings, controller); + if (b) { + if (streq(b->path, new_path)) + return 0; + + if (b->essential) + return -EINVAL; + + 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) { + 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_free_ char *controller = NULL; + CGroupAttribute *a; + CGroupBonding *b; + const char *name; + char **l = NULL; + int r; + + assert(u); + assert(iter); + assert(_result); + + if (!unit_get_exec_context(u)) + return -EINVAL; + + r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &name, false); + if (r < 0) + return r; + + 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_get_path(b->controller, b->path, name, &p); + if (r < 0) + return r; + + r = read_full_file(p, &v, NULL); + if (r >= 0) { + /* Split on new lines */ + l = strv_split_newlines(v); + if (!l) + return -ENOMEM; + + *_result = l; + return 0; + + } + } + + /* 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 **value; + const char *name; + + 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 = bus_parse_strv_iter(iter, &l); + if (r < 0) + return r; + + 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 = 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; + + if (r > 0) { + CGroupBonding *b; + + b = cgroup_bonding_find_list(u->cgroup_bondings, a->controller); + if (!b) { + /* Doesn't exist yet? Then let's add it */ + r = unit_add_cgroup_from_text(u, a->controller, false, &b); + if (r < 0) + return r; + + if (r > 0) { + cgroup_bonding_realize(b); + cgroup_bonding_migrate(b, u->cgroup_bondings); + } + } + + /* Make it count */ + cgroup_attribute_apply(a, u->cgroup_bondings); + } + + } + + 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) { + const char *name; + 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; + + cgroup_attribute_free_some(u->cgroup_attributes, NULL, name); + update_attribute_drop_in(u, runtime, name); + + return 0; } const BusProperty bus_unit_properties[] = { @@ -840,9 +1306,6 @@ const BusProperty bus_unit_properties[] = { { "OnFailureIsolate", bus_property_append_bool, "b", offsetof(Unit, on_failure_isolate) }, { "IgnoreOnIsolate", bus_property_append_bool, "b", offsetof(Unit, ignore_on_isolate) }, { "IgnoreOnSnapshot", bus_property_append_bool, "b", offsetof(Unit, ignore_on_snapshot) }, - { "DefaultControlGroup", bus_unit_append_default_cgroup, "s", 0 }, - { "ControlGroup", bus_unit_append_cgroups, "as", 0 }, - { "ControlGroupAttributes", bus_unit_append_cgroup_attrs,"a(sss)", 0 }, { "NeedDaemonReload", bus_unit_append_need_daemon_reload, "b", 0 }, { "JobTimeoutUSec", bus_property_append_usec, "t", offsetof(Unit, job_timeout) }, { "ConditionTimestamp", bus_property_append_usec, "t", offsetof(Unit, condition_timestamp.realtime) }, @@ -851,3 +1314,10 @@ const BusProperty bus_unit_properties[] = { { "LoadError", bus_unit_append_load_error, "(ss)", 0 }, { NULL, } }; + +const BusProperty bus_unit_cgroup_properties[] = { + { "DefaultControlGroup", bus_unit_append_default_cgroup, "s", 0 }, + { "ControlGroups", bus_unit_append_cgroups, "as", 0 }, + { "ControlGroupAttributes", bus_unit_append_cgroup_attrs, "a(sss)", 0 }, + { NULL, } +};