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 General Public License as published by
10 the Free Software Foundation; either version 2 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 General Public License for more details.
18 You should have received a copy of the GNU 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"
30 const char bus_unit_interface[] _introspect_("Unit") = BUS_UNIT_INTERFACE;
32 const BusProperty bus_unit_properties[] = {
33 { "Id", bus_property_append_string, "s", offsetof(Unit, id), true },
34 { "Names", bus_unit_append_names, "as", 0 },
35 { "Following", bus_unit_append_following, "s", 0 },
36 { "Requires", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRES]), true },
37 { "RequiresOverridable", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRES_OVERRIDABLE]), true },
38 { "Requisite", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUISITE]), true },
39 { "RequisiteOverridable", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUISITE_OVERRIDABLE]), true },
40 { "Wants", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_WANTS]), true },
41 { "BindTo", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BIND_TO]), true },
42 { "RequiredBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY]), true },
43 { "RequiredByOverridable",bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY_OVERRIDABLE]), true },
44 { "WantedBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_WANTED_BY]), true },
45 { "BoundBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BOUND_BY]), true },
46 { "Conflicts", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONFLICTS]), true },
47 { "ConflictedBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONFLICTED_BY]), true },
48 { "Before", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BEFORE]), true },
49 { "After", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_AFTER]), true },
50 { "OnFailure", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_ON_FAILURE]), true },
51 { "Triggers", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_TRIGGERS]), true },
52 { "TriggeredBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_TRIGGERED_BY]), true },
53 { "PropagateReloadTo", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_PROPAGATE_RELOAD_TO]), true },
54 { "PropagateReloadFrom", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_PROPAGATE_RELOAD_FROM]), true },
55 { "Description", bus_unit_append_description, "s", 0 },
56 { "LoadState", bus_unit_append_load_state, "s", offsetof(Unit, load_state) },
57 { "ActiveState", bus_unit_append_active_state, "s", 0 },
58 { "SubState", bus_unit_append_sub_state, "s", 0 },
59 { "FragmentPath", bus_property_append_string, "s", offsetof(Unit, fragment_path), true },
60 { "UnitFileState", bus_unit_append_file_state, "s", 0 },
61 { "InactiveExitTimestamp",bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.realtime) },
62 { "InactiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.monotonic) },
63 { "ActiveEnterTimestamp", bus_property_append_usec, "t", offsetof(Unit, active_enter_timestamp.realtime) },
64 { "ActiveEnterTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, active_enter_timestamp.monotonic) },
65 { "ActiveExitTimestamp", bus_property_append_usec, "t", offsetof(Unit, active_exit_timestamp.realtime) },
66 { "ActiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, active_exit_timestamp.monotonic) },
67 { "InactiveEnterTimestamp", bus_property_append_usec, "t", offsetof(Unit, inactive_enter_timestamp.realtime) },
68 { "InactiveEnterTimestampMonotonic",bus_property_append_usec, "t", offsetof(Unit, inactive_enter_timestamp.monotonic) },
69 { "CanStart", bus_unit_append_can_start, "b", 0 },
70 { "CanStop", bus_unit_append_can_stop, "b", 0 },
71 { "CanReload", bus_unit_append_can_reload, "b", 0 },
72 { "CanIsolate", bus_unit_append_can_isolate, "b", 0 },
73 { "Job", bus_unit_append_job, "(uo)", 0 },
74 { "StopWhenUnneeded", bus_property_append_bool, "b", offsetof(Unit, stop_when_unneeded) },
75 { "RefuseManualStart", bus_property_append_bool, "b", offsetof(Unit, refuse_manual_start) },
76 { "RefuseManualStop", bus_property_append_bool, "b", offsetof(Unit, refuse_manual_stop) },
77 { "AllowIsolate", bus_property_append_bool, "b", offsetof(Unit, allow_isolate) },
78 { "DefaultDependencies", bus_property_append_bool, "b", offsetof(Unit, default_dependencies) },
79 { "OnFailureIsolate", bus_property_append_bool, "b", offsetof(Unit, on_failure_isolate) },
80 { "IgnoreOnIsolate", bus_property_append_bool, "b", offsetof(Unit, ignore_on_isolate) },
81 { "IgnoreOnSnapshot", bus_property_append_bool, "b", offsetof(Unit, ignore_on_snapshot) },
82 { "DefaultControlGroup", bus_unit_append_default_cgroup, "s", 0 },
83 { "ControlGroup", bus_unit_append_cgroups, "as", 0 },
84 { "ControlGroupAttributes", bus_unit_append_cgroup_attrs,"a(sss)", 0 },
85 { "NeedDaemonReload", bus_unit_append_need_daemon_reload, "b", 0 },
86 { "JobTimeoutUSec", bus_property_append_usec, "t", offsetof(Unit, job_timeout) },
87 { "ConditionTimestamp", bus_property_append_usec, "t", offsetof(Unit, condition_timestamp.realtime) },
88 { "ConditionTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, condition_timestamp.monotonic) },
89 { "ConditionResult", bus_property_append_bool, "b", offsetof(Unit, condition_result) },
90 { "LoadError", bus_unit_append_load_error, "(ss)", 0 },
94 #define INVALIDATING_PROPERTIES \
98 "InactiveExitTimestamp\0" \
99 "ActiveEnterTimestamp\0" \
100 "ActiveExitTimestamp\0" \
101 "InactiveEnterTimestamp\0" \
105 int bus_unit_append_names(DBusMessageIter *i, const char *property, void *data) {
111 if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub))
114 SET_FOREACH(t, u->names, j)
115 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &t))
118 if (!dbus_message_iter_close_container(i, &sub))
124 int bus_unit_append_following(DBusMessageIter *i, const char *property, void *data) {
132 f = unit_following(u);
135 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d))
141 int bus_unit_append_dependencies(DBusMessageIter *i, const char *property, void *data) {
147 if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub))
151 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &u->id))
154 if (!dbus_message_iter_close_container(i, &sub))
160 int bus_unit_append_description(DBusMessageIter *i, const char *property, void *data) {
168 d = unit_description(u);
170 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d))
176 DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_unit_append_load_state, unit_load_state, UnitLoadState);
178 int bus_unit_append_active_state(DBusMessageIter *i, const char *property, void *data) {
186 state = unit_active_state_to_string(unit_active_state(u));
188 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
194 int bus_unit_append_sub_state(DBusMessageIter *i, const char *property, void *data) {
202 state = unit_sub_state_to_string(u);
204 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
210 int bus_unit_append_file_state(DBusMessageIter *i, const char *property, void *data) {
218 state = strempty(unit_file_state_to_string(unit_get_unit_file_state(u)));
220 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
226 int bus_unit_append_can_start(DBusMessageIter *i, const char *property, void *data) {
234 b = unit_can_start(u) &&
235 !u->refuse_manual_start;
237 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
243 int bus_unit_append_can_stop(DBusMessageIter *i, const char *property, void *data) {
251 /* On the lower levels we assume that every unit we can start
252 * we can also stop */
254 b = unit_can_start(u) &&
255 !u->refuse_manual_stop;
257 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
263 int bus_unit_append_can_reload(DBusMessageIter *i, const char *property, void *data) {
271 b = unit_can_reload(u);
273 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
279 int bus_unit_append_can_isolate(DBusMessageIter *i, const char *property, void *data) {
287 b = unit_can_isolate(u) &&
288 !u->refuse_manual_start;
290 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
296 int bus_unit_append_job(DBusMessageIter *i, const char *property, void *data) {
305 if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
310 if (!(p = job_dbus_path(u->job)))
313 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &u->job->id) ||
314 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p)) {
321 /* No job, so let's fill in some placeholder
322 * data. Since we need to fill in a valid path we
323 * simple point to ourselves. */
325 if (!(p = unit_dbus_path(u)))
328 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &id) ||
329 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p)) {
337 if (!dbus_message_iter_close_container(i, &sub))
343 int bus_unit_append_default_cgroup(DBusMessageIter *i, const char *property, void *data) {
353 if ((cgb = unit_get_default_cgroup(u))) {
354 if (!(t = cgroup_bonding_to_string(cgb)))
359 success = dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t);
364 return success ? 0 : -ENOMEM;
367 int bus_unit_append_cgroups(DBusMessageIter *i, const char *property, void *data) {
372 if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub))
375 LIST_FOREACH(by_unit, cgb, u->cgroup_bondings) {
379 if (!(t = cgroup_bonding_to_string(cgb)))
382 success = dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &t);
389 if (!dbus_message_iter_close_container(i, &sub))
395 int bus_unit_append_cgroup_attrs(DBusMessageIter *i, const char *property, void *data) {
398 DBusMessageIter sub, sub2;
400 if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(sss)", &sub))
403 LIST_FOREACH(by_unit, a, u->cgroup_attributes) {
408 a->map_callback(a->controller, a->name, a->value, &v);
411 dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) &&
412 dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &a->controller) &&
413 dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &a->name) &&
414 dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, v ? &v : &a->value) &&
415 dbus_message_iter_close_container(&sub, &sub2);
423 if (!dbus_message_iter_close_container(i, &sub))
429 int bus_unit_append_need_daemon_reload(DBusMessageIter *i, const char *property, void *data) {
437 b = unit_need_daemon_reload(u);
439 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
445 int bus_unit_append_load_error(DBusMessageIter *i, const char *property, void *data) {
447 const char *name, *message;
454 if (u->load_error != 0) {
455 name = bus_errno_to_dbus(u->load_error);
456 message = strempty(strerror(-u->load_error));
460 if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub) ||
461 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &name) ||
462 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &message) ||
463 !dbus_message_iter_close_container(i, &sub))
469 static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *connection, DBusMessage *message) {
470 DBusMessage *reply = NULL;
471 Manager *m = u->manager;
473 JobType job_type = _JOB_TYPE_INVALID;
475 bool reload_if_possible = false;
477 dbus_error_init(&error);
479 if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Start"))
480 job_type = JOB_START;
481 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Stop"))
483 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Reload"))
484 job_type = JOB_RELOAD;
485 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Restart"))
486 job_type = JOB_RESTART;
487 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "TryRestart"))
488 job_type = JOB_TRY_RESTART;
489 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ReloadOrRestart")) {
490 reload_if_possible = true;
491 job_type = JOB_RESTART;
492 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ReloadOrTryRestart")) {
493 reload_if_possible = true;
494 job_type = JOB_TRY_RESTART;
495 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Kill")) {
496 const char *swho, *smode;
502 if (!dbus_message_get_args(
505 DBUS_TYPE_STRING, &swho,
506 DBUS_TYPE_STRING, &smode,
507 DBUS_TYPE_INT32, &signo,
509 return bus_send_error_reply(connection, message, &error, -EINVAL);
514 who = kill_who_from_string(swho);
516 return bus_send_error_reply(connection, message, &error, -EINVAL);
520 mode = KILL_CONTROL_GROUP;
522 mode = kill_mode_from_string(smode);
524 return bus_send_error_reply(connection, message, &error, -EINVAL);
527 if (signo <= 0 || signo >= _NSIG)
528 return bus_send_error_reply(connection, message, &error, -EINVAL);
530 if ((r = unit_kill(u, who, mode, signo, &error)) < 0)
531 return bus_send_error_reply(connection, message, &error, r);
533 if (!(reply = dbus_message_new_method_return(message)))
536 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ResetFailed")) {
538 unit_reset_failed(u);
540 if (!(reply = dbus_message_new_method_return(message)))
543 } else if (UNIT_VTABLE(u)->bus_message_handler)
544 return UNIT_VTABLE(u)->bus_message_handler(u, connection, message);
546 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
548 if (job_type != _JOB_TYPE_INVALID) {
554 if ((job_type == JOB_START && u->refuse_manual_start) ||
555 (job_type == JOB_STOP && u->refuse_manual_stop) ||
556 ((job_type == JOB_RESTART || job_type == JOB_TRY_RESTART) &&
557 (u->refuse_manual_start || u->refuse_manual_stop))) {
558 dbus_set_error(&error, BUS_ERROR_ONLY_BY_DEPENDENCY, "Operation refused, may be requested by dependency only.");
559 return bus_send_error_reply(connection, message, &error, -EPERM);
562 if (!dbus_message_get_args(
565 DBUS_TYPE_STRING, &smode,
567 return bus_send_error_reply(connection, message, &error, -EINVAL);
569 if (reload_if_possible && unit_can_reload(u)) {
570 if (job_type == JOB_RESTART)
571 job_type = JOB_RELOAD_OR_START;
572 else if (job_type == JOB_TRY_RESTART)
573 job_type = JOB_RELOAD;
576 if ((mode = job_mode_from_string(smode)) == _JOB_MODE_INVALID) {
577 dbus_set_error(&error, BUS_ERROR_INVALID_JOB_MODE, "Job mode %s is invalid.", smode);
578 return bus_send_error_reply(connection, message, &error, -EINVAL);
581 if ((r = manager_add_job(m, job_type, u, mode, true, &error, &j)) < 0)
582 return bus_send_error_reply(connection, message, &error, r);
584 if (!(reply = dbus_message_new_method_return(message)))
587 if (!(path = job_dbus_path(j)))
590 if (!dbus_message_append_args(
592 DBUS_TYPE_OBJECT_PATH, &path,
598 if (!dbus_connection_send(connection, reply, NULL))
601 dbus_message_unref(reply);
606 return DBUS_HANDLER_RESULT_HANDLED;
612 dbus_message_unref(reply);
614 dbus_error_free(&error);
616 return DBUS_HANDLER_RESULT_NEED_MEMORY;
619 static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DBusMessage *message, void *data) {
629 if (streq(dbus_message_get_path(message), "/org/freedesktop/systemd1/unit")) {
630 /* Be nice to gdbus and return introspection data for our mid-level paths */
632 if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
633 char *introspection = NULL;
639 if (!(reply = dbus_message_new_method_return(message)))
642 /* We roll our own introspection code here, instead of
643 * relying on bus_default_message_handler() because we
644 * need to generate our introspection string
647 if (!(f = open_memstream(&introspection, &size)))
650 fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
653 fputs(BUS_INTROSPECTABLE_INTERFACE, f);
654 fputs(BUS_PEER_INTERFACE, f);
656 HASHMAP_FOREACH_KEY(u, k, m->units, i) {
662 if (!(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 (!dbus_connection_send(connection, reply, NULL))
695 dbus_message_unref(reply);
697 return DBUS_HANDLER_RESULT_HANDLED;
700 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
703 if ((r = manager_get_unit_from_dbus_path(m, dbus_message_get_path(message), &u)) < 0) {
706 return DBUS_HANDLER_RESULT_NEED_MEMORY;
712 dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown unit");
713 return bus_send_error_reply(connection, message, &e, r);
716 return bus_send_error_reply(connection, message, NULL, r);
719 return bus_unit_message_dispatch(u, connection, message);
723 dbus_message_unref(reply);
725 return DBUS_HANDLER_RESULT_NEED_MEMORY;
728 const DBusObjectPathVTable bus_unit_vtable = {
729 .message_function = bus_unit_message_handler
732 void bus_unit_send_change_signal(Unit *u) {
734 DBusMessage *m = NULL;
738 if (u->in_dbus_queue) {
739 LIST_REMOVE(Unit, dbus_queue, u->manager->dbus_unit_queue, u);
740 u->in_dbus_queue = false;
746 if (!bus_has_subscriber(u->manager)) {
747 u->sent_dbus_new_signal = true;
751 if (!(p = unit_dbus_path(u)))
754 if (u->sent_dbus_new_signal) {
755 /* Send a properties changed signal. First for the
756 * specific type, then for the generic unit. The
757 * clients may rely on this order to get atomic
758 * behaviour if needed. */
760 if (UNIT_VTABLE(u)->bus_invalidating_properties) {
762 if (!(m = bus_properties_changed_new(p,
763 UNIT_VTABLE(u)->bus_interface,
764 UNIT_VTABLE(u)->bus_invalidating_properties)))
767 if (bus_broadcast(u->manager, m) < 0)
770 dbus_message_unref(m);
773 if (!(m = bus_properties_changed_new(p, "org.freedesktop.systemd1.Unit", INVALIDATING_PROPERTIES)))
777 /* Send a new signal */
779 if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "UnitNew")))
782 if (!dbus_message_append_args(m,
783 DBUS_TYPE_STRING, &u->id,
784 DBUS_TYPE_OBJECT_PATH, &p,
789 if (bus_broadcast(u->manager, m) < 0)
793 dbus_message_unref(m);
795 u->sent_dbus_new_signal = true;
803 dbus_message_unref(m);
805 log_error("Failed to allocate unit change/new signal.");
808 void bus_unit_send_removed_signal(Unit *u) {
810 DBusMessage *m = NULL;
814 if (!bus_has_subscriber(u->manager))
817 if (!u->sent_dbus_new_signal)
818 bus_unit_send_change_signal(u);
823 if (!(p = unit_dbus_path(u)))
826 if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "UnitRemoved")))
829 if (!dbus_message_append_args(m,
830 DBUS_TYPE_STRING, &u->id,
831 DBUS_TYPE_OBJECT_PATH, &p,
835 if (bus_broadcast(u->manager, m) < 0)
839 dbus_message_unref(m);
847 dbus_message_unref(m);
849 log_error("Failed to allocate unit remove signal.");