1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
26 #include "dbus-unit.h"
27 #include "bus-errors.h"
28 #include "dbus-common.h"
29 #include "selinux-access.h"
30 #include "cgroup-util.h"
32 #include "path-util.h"
35 const char bus_unit_interface[] _introspect_("Unit") = BUS_UNIT_INTERFACE;
37 #define INVALIDATING_PROPERTIES \
41 "InactiveExitTimestamp\0" \
42 "ActiveEnterTimestamp\0" \
43 "ActiveExitTimestamp\0" \
44 "InactiveEnterTimestamp\0" \
48 static int bus_unit_append_names(DBusMessageIter *i, const char *property, void *data) {
54 if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub))
57 SET_FOREACH(t, u->names, j)
58 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &t))
61 if (!dbus_message_iter_close_container(i, &sub))
67 static int bus_unit_append_following(DBusMessageIter *i, const char *property, void *data) {
75 f = unit_following(u);
78 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d))
84 static int bus_unit_append_slice(DBusMessageIter *i, const char *property, void *data) {
92 d = strempty(unit_slice_name(u));
94 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d))
100 static int bus_unit_append_dependencies(DBusMessageIter *i, const char *property, void *data) {
106 if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub))
110 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &u->id))
113 if (!dbus_message_iter_close_container(i, &sub))
119 static int bus_unit_append_description(DBusMessageIter *i, const char *property, void *data) {
127 d = unit_description(u);
129 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d))
135 static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_unit_append_load_state, unit_load_state, UnitLoadState);
137 static int bus_unit_append_active_state(DBusMessageIter *i, const char *property, void *data) {
145 state = unit_active_state_to_string(unit_active_state(u));
147 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
153 static int bus_unit_append_sub_state(DBusMessageIter *i, const char *property, void *data) {
161 state = unit_sub_state_to_string(u);
163 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
169 static int bus_unit_append_file_state(DBusMessageIter *i, const char *property, void *data) {
177 state = strempty(unit_file_state_to_string(unit_get_unit_file_state(u)));
179 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
185 static int bus_unit_append_can_start(DBusMessageIter *i, const char *property, void *data) {
193 b = unit_can_start(u) &&
194 !u->refuse_manual_start;
196 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
202 static int bus_unit_append_can_stop(DBusMessageIter *i, const char *property, void *data) {
210 /* On the lower levels we assume that every unit we can start
211 * we can also stop */
213 b = unit_can_start(u) &&
214 !u->refuse_manual_stop;
216 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
222 static int bus_unit_append_can_reload(DBusMessageIter *i, const char *property, void *data) {
230 b = unit_can_reload(u);
232 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
238 static int bus_unit_append_can_isolate(DBusMessageIter *i, const char *property, void *data) {
246 b = unit_can_isolate(u) &&
247 !u->refuse_manual_start;
249 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
255 static int bus_unit_append_job(DBusMessageIter *i, const char *property, void *data) {
258 _cleanup_free_ char *p = NULL;
264 if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
269 p = job_dbus_path(u->job);
273 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &u->job->id) ||
274 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p))
279 /* No job, so let's fill in some placeholder
280 * data. Since we need to fill in a valid path we
281 * simple point to ourselves. */
283 p = unit_dbus_path(u);
287 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &id) ||
288 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p))
292 if (!dbus_message_iter_close_container(i, &sub))
298 static int bus_unit_append_default_cgroup(DBusMessageIter *i, const char *property, void *data) {
308 cgb = unit_get_default_cgroup(u);
310 t = cgroup_bonding_to_string(cgb);
316 success = dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t);
321 return success ? 0 : -ENOMEM;
324 static int bus_unit_append_cgroups(DBusMessageIter *i, const char *property, void *data) {
329 if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub))
332 LIST_FOREACH(by_unit, cgb, u->cgroup_bondings) {
333 _cleanup_free_ char *t = NULL;
336 t = cgroup_bonding_to_string(cgb);
340 success = dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &t);
345 if (!dbus_message_iter_close_container(i, &sub))
351 static int bus_unit_append_cgroup_attrs(DBusMessageIter *i, const char *property, void *data) {
354 DBusMessageIter sub, sub2;
356 if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(sss)", &sub))
359 LIST_FOREACH(by_unit, a, u->cgroup_attributes) {
360 _cleanup_free_ char *v = NULL;
363 if (a->semantics && a->semantics->map_write)
364 a->semantics->map_write(a->semantics, a->value, &v);
367 dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) &&
368 dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &a->controller) &&
369 dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &a->name) &&
370 dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, v ? &v : &a->value) &&
371 dbus_message_iter_close_container(&sub, &sub2);
376 if (!dbus_message_iter_close_container(i, &sub))
382 static int bus_unit_append_need_daemon_reload(DBusMessageIter *i, const char *property, void *data) {
390 b = unit_need_daemon_reload(u);
392 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
398 static int bus_unit_append_load_error(DBusMessageIter *i, const char *property, void *data) {
400 const char *name, *message;
407 if (u->load_error != 0) {
408 name = bus_errno_to_dbus(u->load_error);
409 message = strempty(strerror(-u->load_error));
413 if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub) ||
414 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &name) ||
415 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &message) ||
416 !dbus_message_iter_close_container(i, &sub))
422 static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *connection, DBusMessage *message) {
423 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
425 JobType job_type = _JOB_TYPE_INVALID;
426 bool reload_if_possible = false;
429 dbus_error_init(&error);
431 if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Start"))
432 job_type = JOB_START;
433 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Stop"))
435 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Reload"))
436 job_type = JOB_RELOAD;
437 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Restart"))
438 job_type = JOB_RESTART;
439 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "TryRestart"))
440 job_type = JOB_TRY_RESTART;
441 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ReloadOrRestart")) {
442 reload_if_possible = true;
443 job_type = JOB_RESTART;
444 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ReloadOrTryRestart")) {
445 reload_if_possible = true;
446 job_type = JOB_TRY_RESTART;
447 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Kill")) {
452 if (!dbus_message_get_args(
455 DBUS_TYPE_STRING, &swho,
456 DBUS_TYPE_INT32, &signo,
458 return bus_send_error_reply(connection, message, &error, -EINVAL);
463 who = kill_who_from_string(swho);
465 return bus_send_error_reply(connection, message, &error, -EINVAL);
468 if (signo <= 0 || signo >= _NSIG)
469 return bus_send_error_reply(connection, message, &error, -EINVAL);
471 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
473 r = unit_kill(u, who, signo, &error);
475 return bus_send_error_reply(connection, message, &error, r);
477 reply = dbus_message_new_method_return(message);
481 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ResetFailed")) {
483 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "reload");
485 unit_reset_failed(u);
487 reply = dbus_message_new_method_return(message);
491 } else if (streq_ptr(dbus_message_get_member(message), "SetControlGroup")) {
492 DBusMessageIter iter;
494 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "start");
496 if (!dbus_message_iter_init(message, &iter))
499 r = bus_unit_cgroup_set(u, &iter);
501 return bus_send_error_reply(connection, message, NULL, r);
503 reply = dbus_message_new_method_return(message);
507 } else if (streq_ptr(dbus_message_get_member(message), "UnsetControlGroup")) {
508 DBusMessageIter iter;
510 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
512 if (!dbus_message_iter_init(message, &iter))
515 r = bus_unit_cgroup_unset(u, &iter);
517 return bus_send_error_reply(connection, message, NULL, r);
519 reply = dbus_message_new_method_return(message);
522 } else if (streq_ptr(dbus_message_get_member(message), "GetControlGroupAttribute")) {
523 DBusMessageIter iter;
524 _cleanup_strv_free_ char **list = NULL;
526 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "status");
528 if (!dbus_message_iter_init(message, &iter))
531 r = bus_unit_cgroup_attribute_get(u, &iter, &list);
533 return bus_send_error_reply(connection, message, NULL, r);
535 reply = dbus_message_new_method_return(message);
539 dbus_message_iter_init_append(reply, &iter);
540 if (bus_append_strv_iter(&iter, list) < 0)
543 } else if (streq_ptr(dbus_message_get_member(message), "SetControlGroupAttribute")) {
544 DBusMessageIter iter;
546 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "start");
548 if (!dbus_message_iter_init(message, &iter))
551 r = bus_unit_cgroup_attribute_set(u, &iter);
553 return bus_send_error_reply(connection, message, NULL, r);
555 reply = dbus_message_new_method_return(message);
559 } else if (streq_ptr(dbus_message_get_member(message), "UnsetControlGroupAttribute")) {
560 DBusMessageIter iter;
562 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
564 if (!dbus_message_iter_init(message, &iter))
567 r = bus_unit_cgroup_attribute_unset(u, &iter);
569 return bus_send_error_reply(connection, message, NULL, r);
571 reply = dbus_message_new_method_return(message);
575 } else if (UNIT_VTABLE(u)->bus_message_handler)
576 return UNIT_VTABLE(u)->bus_message_handler(u, connection, message);
578 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
580 if (job_type != _JOB_TYPE_INVALID) {
584 if (!dbus_message_get_args(
587 DBUS_TYPE_STRING, &smode,
589 return bus_send_error_reply(connection, message, &error, -EINVAL);
591 mode = job_mode_from_string(smode);
593 dbus_set_error(&error, BUS_ERROR_INVALID_JOB_MODE, "Job mode %s is invalid.", smode);
594 return bus_send_error_reply(connection, message, &error, -EINVAL);
597 return bus_unit_queue_job(connection, message, u, job_type, mode, reload_if_possible);
601 if (!bus_maybe_send_reply(connection, message, reply))
604 return DBUS_HANDLER_RESULT_HANDLED;
607 dbus_error_free(&error);
608 return DBUS_HANDLER_RESULT_NEED_MEMORY;
611 static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DBusMessage *message, void *data) {
615 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
622 dbus_error_init(&error);
624 if (streq(dbus_message_get_path(message), "/org/freedesktop/systemd1/unit")) {
625 /* Be nice to gdbus and return introspection data for our mid-level paths */
627 SELINUX_ACCESS_CHECK(connection, message, "status");
629 if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
630 char *introspection = NULL;
636 reply = dbus_message_new_method_return(message);
640 /* We roll our own introspection code here, instead of
641 * relying on bus_default_message_handler() because we
642 * need to generate our introspection string
645 f = open_memstream(&introspection, &size);
649 fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
652 fputs(BUS_INTROSPECTABLE_INTERFACE, f);
653 fputs(BUS_PEER_INTERFACE, f);
655 HASHMAP_FOREACH_KEY(u, k, m->units, i) {
661 p = bus_path_escape(k);
668 fprintf(f, "<node name=\"%s\"/>", p);
672 fputs("</node>\n", f);
685 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
692 if (!bus_maybe_send_reply(connection, message, reply))
695 return DBUS_HANDLER_RESULT_HANDLED;
698 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
701 r = manager_load_unit_from_dbus_path(m, dbus_message_get_path(message), &error, &u);
705 return bus_send_error_reply(connection, message, &error, r);
707 return bus_unit_message_dispatch(u, connection, message);
710 dbus_error_free(&error);
712 return DBUS_HANDLER_RESULT_NEED_MEMORY;
715 const DBusObjectPathVTable bus_unit_vtable = {
716 .message_function = bus_unit_message_handler
719 void bus_unit_send_change_signal(Unit *u) {
720 _cleanup_free_ char *p = NULL;
721 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
725 if (u->in_dbus_queue) {
726 LIST_REMOVE(Unit, dbus_queue, u->manager->dbus_unit_queue, u);
727 u->in_dbus_queue = false;
733 if (!bus_has_subscriber(u->manager)) {
734 u->sent_dbus_new_signal = true;
738 p = unit_dbus_path(u);
742 if (u->sent_dbus_new_signal) {
743 /* Send a properties changed signal. First for the
744 * specific type, then for the generic unit. The
745 * clients may rely on this order to get atomic
746 * behavior if needed. */
748 if (UNIT_VTABLE(u)->bus_invalidating_properties) {
750 m = bus_properties_changed_new(p,
751 UNIT_VTABLE(u)->bus_interface,
752 UNIT_VTABLE(u)->bus_invalidating_properties);
756 if (bus_broadcast(u->manager, m) < 0)
759 dbus_message_unref(m);
762 m = bus_properties_changed_new(p, "org.freedesktop.systemd1.Unit",
763 INVALIDATING_PROPERTIES);
768 /* Send a new signal */
770 m = dbus_message_new_signal("/org/freedesktop/systemd1",
771 "org.freedesktop.systemd1.Manager",
776 if (!dbus_message_append_args(m,
777 DBUS_TYPE_STRING, &u->id,
778 DBUS_TYPE_OBJECT_PATH, &p,
783 if (bus_broadcast(u->manager, m) < 0)
786 u->sent_dbus_new_signal = true;
794 void bus_unit_send_removed_signal(Unit *u) {
795 _cleanup_free_ char *p = NULL;
796 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
800 if (!bus_has_subscriber(u->manager))
803 if (!u->sent_dbus_new_signal)
804 bus_unit_send_change_signal(u);
809 p = unit_dbus_path(u);
813 m = dbus_message_new_signal("/org/freedesktop/systemd1",
814 "org.freedesktop.systemd1.Manager",
819 if (!dbus_message_append_args(m,
820 DBUS_TYPE_STRING, &u->id,
821 DBUS_TYPE_OBJECT_PATH, &p,
825 if (bus_broadcast(u->manager, m) < 0)
834 DBusHandlerResult bus_unit_queue_job(
835 DBusConnection *connection,
836 DBusMessage *message,
840 bool reload_if_possible) {
842 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
843 _cleanup_free_ char *path = NULL;
852 assert(type >= 0 && type < _JOB_TYPE_MAX);
853 assert(mode >= 0 && mode < _JOB_MODE_MAX);
855 dbus_error_init(&error);
857 if (reload_if_possible && unit_can_reload(u)) {
858 if (type == JOB_RESTART)
859 type = JOB_RELOAD_OR_START;
860 else if (type == JOB_TRY_RESTART)
864 SELINUX_UNIT_ACCESS_CHECK(u, connection, message,
865 (type == JOB_START || type == JOB_RESTART || type == JOB_TRY_RESTART) ? "start" :
866 type == JOB_STOP ? "stop" : "reload");
868 if (type == JOB_STOP && u->load_state == UNIT_ERROR && unit_active_state(u) == UNIT_INACTIVE) {
869 dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s not loaded.", u->id);
870 return bus_send_error_reply(connection, message, &error, -EPERM);
873 if ((type == JOB_START && u->refuse_manual_start) ||
874 (type == JOB_STOP && u->refuse_manual_stop) ||
875 ((type == JOB_RESTART || type == JOB_TRY_RESTART) && (u->refuse_manual_start || u->refuse_manual_stop))) {
876 dbus_set_error(&error, BUS_ERROR_ONLY_BY_DEPENDENCY,
877 "Operation refused, unit %s may be requested by dependency only.", u->id);
878 return bus_send_error_reply(connection, message, &error, -EPERM);
881 r = manager_add_job(u->manager, type, u, mode, true, &error, &j);
883 return bus_send_error_reply(connection, message, &error, r);
885 cl = job_bus_client_new(connection, bus_message_get_sender_with_fallback(message));
889 LIST_PREPEND(JobBusClient, client, j->bus_client_list, cl);
891 reply = dbus_message_new_method_return(message);
895 path = job_dbus_path(j);
899 if (!dbus_message_append_args(
901 DBUS_TYPE_OBJECT_PATH, &path,
905 if (!bus_maybe_send_reply(connection, message, reply))
908 return DBUS_HANDLER_RESULT_HANDLED;
911 dbus_error_free(&error);
913 return DBUS_HANDLER_RESULT_NEED_MEMORY;
916 static int parse_mode(DBusMessageIter *iter, bool *runtime, bool next) {
923 r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &mode, next);
927 if (streq(mode, "runtime"))
929 else if (streq(mode, "persistent"))
937 int bus_unit_cgroup_set(Unit *u, DBusMessageIter *iter) {
938 _cleanup_free_ char *controller = NULL, *old_path = NULL, *new_path = NULL, *contents = NULL;
947 if (!unit_get_exec_context(u))
950 r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &name, true);
954 r = parse_mode(iter, &runtime, false);
958 r = cg_split_spec(name, &controller, &new_path);
963 new_path = unit_default_cgroup_path(u);
968 if (!controller || streq(controller, SYSTEMD_CGROUP_CONTROLLER))
971 b = cgroup_bonding_find_list(u->cgroup_bondings, controller);
973 if (streq(b->path, new_path))
979 old_path = strdup(b->path);
984 r = unit_add_cgroup_from_text(u, name, true, &b);
990 /* Try to move things to the new place, and clean up the old place */
991 cgroup_bonding_realize(b);
992 cgroup_bonding_migrate(b, u->cgroup_bondings);
995 cg_trim(controller, old_path, true);
997 /* Apply the attributes to the new group */
998 LIST_FOREACH(by_unit, a, u->cgroup_attributes)
999 if (streq(a->controller, controller))
1000 cgroup_attribute_apply(a, b);
1003 contents = strjoin("[", UNIT_VTABLE(u)->exec_section, "]\n"
1004 "ControlGroup=", name, "\n", NULL);
1008 return unit_write_drop_in(u, runtime, controller, contents);
1011 int bus_unit_cgroup_unset(Unit *u, DBusMessageIter *iter) {
1012 _cleanup_free_ char *controller = NULL, *path = NULL, *target = NULL;
1014 CGroupAttribute *a, *n;
1022 if (!unit_get_exec_context(u))
1025 r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &name, true);
1029 r = parse_mode(iter, &runtime, false);
1033 r = cg_split_spec(name, &controller, &path);
1037 if (!controller || streq(controller, SYSTEMD_CGROUP_CONTROLLER))
1040 b = cgroup_bonding_find_list(u->cgroup_bondings, controller);
1044 if (path && !path_equal(path, b->path))
1050 unit_remove_drop_in(u, runtime, controller);
1052 /* Try to migrate the old group away */
1053 if (cg_pid_get_path(controller, 0, &target) >= 0)
1054 cgroup_bonding_migrate_to(u->cgroup_bondings, target, false);
1056 cgroup_bonding_free(b, true);
1058 /* Drop all attributes of this controller */
1059 LIST_FOREACH_SAFE(by_unit, a, n, u->cgroup_attributes) {
1060 if (!streq(a->controller, controller))
1063 unit_remove_drop_in(u, runtime, a->name);
1064 cgroup_attribute_free(a);
1070 int bus_unit_cgroup_attribute_get(Unit *u, DBusMessageIter *iter, char ***_result) {
1071 _cleanup_free_ char *controller = NULL;
1082 if (!unit_get_exec_context(u))
1085 r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &name, false);
1089 r = cg_controller_from_attr(name, &controller);
1093 /* First attempt, read the value from the kernel */
1094 b = cgroup_bonding_find_list(u->cgroup_bondings, controller);
1096 _cleanup_free_ char *p = NULL, *v = NULL;
1098 r = cg_get_path(b->controller, b->path, name, &p);
1102 r = read_full_file(p, &v, NULL);
1104 /* Split on new lines */
1105 l = strv_split_newlines(v);
1115 /* If that didn't work, read our cached value */
1116 LIST_FOREACH(by_unit, a, u->cgroup_attributes) {
1118 if (!cgroup_attribute_matches(a, controller, name))
1121 r = strv_extend(&l, a->value);
1135 static int update_attribute_drop_in(Unit *u, bool runtime, const char *name) {
1136 _cleanup_free_ char *buf = NULL;
1142 LIST_FOREACH(by_unit, a, u->cgroup_attributes) {
1143 if (!cgroup_attribute_matches(a, NULL, name))
1147 buf = strjoin("[", UNIT_VTABLE(u)->exec_section, "]\n"
1148 "ControlGroupAttribute=", a->name, " ", a->value, "\n", NULL);
1156 "ControlGroupAttribute=", a->name, " ", a->value, "\n", NULL);
1167 return unit_write_drop_in(u, runtime, name, buf);
1169 return unit_remove_drop_in(u, runtime, name);
1172 int bus_unit_cgroup_attribute_set(Unit *u, DBusMessageIter *iter) {
1173 _cleanup_strv_free_ char **l = NULL;
1175 bool runtime = false;
1182 if (!unit_get_exec_context(u))
1185 r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &name, true);
1189 r = bus_parse_strv_iter(iter, &l);
1193 if (!dbus_message_iter_next(iter))
1196 r = parse_mode(iter, &runtime, false);
1200 STRV_FOREACH(value, l) {
1201 _cleanup_free_ char *v = NULL;
1203 const CGroupSemantics *s;
1205 r = cgroup_semantics_find(NULL, name, *value, &v, &s);
1209 if (s && !s->multiple && l[1])
1212 r = unit_add_cgroup_attribute(u, s, NULL, name, v ? v : *value, &a);
1219 b = cgroup_bonding_find_list(u->cgroup_bondings, a->controller);
1221 /* Doesn't exist yet? Then let's add it */
1222 r = unit_add_cgroup_from_text(u, a->controller, false, &b);
1227 cgroup_bonding_realize(b);
1228 cgroup_bonding_migrate(b, u->cgroup_bondings);
1233 cgroup_attribute_apply(a, u->cgroup_bondings);
1238 r = update_attribute_drop_in(u, runtime, name);
1245 int bus_unit_cgroup_attribute_unset(Unit *u, DBusMessageIter *iter) {
1253 if (!unit_get_exec_context(u))
1256 r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &name, true);
1260 r = parse_mode(iter, &runtime, false);
1264 cgroup_attribute_free_some(u->cgroup_attributes, NULL, name);
1265 update_attribute_drop_in(u, runtime, name);
1270 const BusProperty bus_unit_properties[] = {
1271 { "Id", bus_property_append_string, "s", offsetof(Unit, id), true },
1272 { "Names", bus_unit_append_names, "as", 0 },
1273 { "Following", bus_unit_append_following, "s", 0 },
1274 { "Slice", bus_unit_append_slice, "s", 0 },
1275 { "Requires", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRES]), true },
1276 { "RequiresOverridable", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRES_OVERRIDABLE]), true },
1277 { "Requisite", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUISITE]), true },
1278 { "RequisiteOverridable", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUISITE_OVERRIDABLE]), true },
1279 { "Wants", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_WANTS]), true },
1280 { "BindsTo", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BINDS_TO]), true },
1281 { "PartOf", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_PART_OF]), true },
1282 { "RequiredBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY]), true },
1283 { "RequiredByOverridable",bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY_OVERRIDABLE]), true },
1284 { "WantedBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_WANTED_BY]), true },
1285 { "BoundBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BOUND_BY]), true },
1286 { "ConsistsOf", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONSISTS_OF]), true },
1287 { "Conflicts", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONFLICTS]), true },
1288 { "ConflictedBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONFLICTED_BY]), true },
1289 { "Before", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BEFORE]), true },
1290 { "After", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_AFTER]), true },
1291 { "OnFailure", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_ON_FAILURE]), true },
1292 { "Triggers", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_TRIGGERS]), true },
1293 { "TriggeredBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_TRIGGERED_BY]), true },
1294 { "PropagatesReloadTo", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_PROPAGATES_RELOAD_TO]), true },
1295 { "ReloadPropagatedFrom", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_RELOAD_PROPAGATED_FROM]), true },
1296 { "RequiresMountsFor", bus_property_append_strv, "as", offsetof(Unit, requires_mounts_for), true },
1297 { "Documentation", bus_property_append_strv, "as", offsetof(Unit, documentation), true },
1298 { "Description", bus_unit_append_description, "s", 0 },
1299 { "LoadState", bus_unit_append_load_state, "s", offsetof(Unit, load_state) },
1300 { "ActiveState", bus_unit_append_active_state, "s", 0 },
1301 { "SubState", bus_unit_append_sub_state, "s", 0 },
1302 { "FragmentPath", bus_property_append_string, "s", offsetof(Unit, fragment_path), true },
1303 { "SourcePath", bus_property_append_string, "s", offsetof(Unit, source_path), true },
1304 { "DropInPaths", bus_property_append_strv, "as", offsetof(Unit, dropin_paths), true },
1305 { "UnitFileState", bus_unit_append_file_state, "s", 0 },
1306 { "InactiveExitTimestamp",bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.realtime) },
1307 { "InactiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.monotonic) },
1308 { "ActiveEnterTimestamp", bus_property_append_usec, "t", offsetof(Unit, active_enter_timestamp.realtime) },
1309 { "ActiveEnterTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, active_enter_timestamp.monotonic) },
1310 { "ActiveExitTimestamp", bus_property_append_usec, "t", offsetof(Unit, active_exit_timestamp.realtime) },
1311 { "ActiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, active_exit_timestamp.monotonic) },
1312 { "InactiveEnterTimestamp", bus_property_append_usec, "t", offsetof(Unit, inactive_enter_timestamp.realtime) },
1313 { "InactiveEnterTimestampMonotonic",bus_property_append_usec, "t", offsetof(Unit, inactive_enter_timestamp.monotonic) },
1314 { "CanStart", bus_unit_append_can_start, "b", 0 },
1315 { "CanStop", bus_unit_append_can_stop, "b", 0 },
1316 { "CanReload", bus_unit_append_can_reload, "b", 0 },
1317 { "CanIsolate", bus_unit_append_can_isolate, "b", 0 },
1318 { "Job", bus_unit_append_job, "(uo)", 0 },
1319 { "StopWhenUnneeded", bus_property_append_bool, "b", offsetof(Unit, stop_when_unneeded) },
1320 { "RefuseManualStart", bus_property_append_bool, "b", offsetof(Unit, refuse_manual_start) },
1321 { "RefuseManualStop", bus_property_append_bool, "b", offsetof(Unit, refuse_manual_stop) },
1322 { "AllowIsolate", bus_property_append_bool, "b", offsetof(Unit, allow_isolate) },
1323 { "DefaultDependencies", bus_property_append_bool, "b", offsetof(Unit, default_dependencies) },
1324 { "OnFailureIsolate", bus_property_append_bool, "b", offsetof(Unit, on_failure_isolate) },
1325 { "IgnoreOnIsolate", bus_property_append_bool, "b", offsetof(Unit, ignore_on_isolate) },
1326 { "IgnoreOnSnapshot", bus_property_append_bool, "b", offsetof(Unit, ignore_on_snapshot) },
1327 { "NeedDaemonReload", bus_unit_append_need_daemon_reload, "b", 0 },
1328 { "JobTimeoutUSec", bus_property_append_usec, "t", offsetof(Unit, job_timeout) },
1329 { "ConditionTimestamp", bus_property_append_usec, "t", offsetof(Unit, condition_timestamp.realtime) },
1330 { "ConditionTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, condition_timestamp.monotonic) },
1331 { "ConditionResult", bus_property_append_bool, "b", offsetof(Unit, condition_result) },
1332 { "LoadError", bus_unit_append_load_error, "(ss)", 0 },
1336 const BusProperty bus_unit_cgroup_properties[] = {
1337 { "DefaultControlGroup", bus_unit_append_default_cgroup, "s", 0 },
1338 { "ControlGroups", bus_unit_append_cgroups, "as", 0 },
1339 { "ControlGroupAttributes", bus_unit_append_cgroup_attrs, "a(sss)", 0 },