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"
30 const char bus_unit_interface[] _introspect_("Unit") = BUS_UNIT_INTERFACE;
32 #define INVALIDATING_PROPERTIES \
36 "InactiveExitTimestamp\0" \
37 "ActiveEnterTimestamp\0" \
38 "ActiveExitTimestamp\0" \
39 "InactiveEnterTimestamp\0" \
43 static int bus_unit_append_names(DBusMessageIter *i, const char *property, void *data) {
49 if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub))
52 SET_FOREACH(t, u->names, j)
53 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &t))
56 if (!dbus_message_iter_close_container(i, &sub))
62 static int bus_unit_append_following(DBusMessageIter *i, const char *property, void *data) {
70 f = unit_following(u);
73 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d))
79 static int bus_unit_append_dependencies(DBusMessageIter *i, const char *property, void *data) {
85 if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub))
89 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &u->id))
92 if (!dbus_message_iter_close_container(i, &sub))
98 static int bus_unit_append_description(DBusMessageIter *i, const char *property, void *data) {
106 d = unit_description(u);
108 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d))
114 static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_unit_append_load_state, unit_load_state, UnitLoadState);
116 static int bus_unit_append_active_state(DBusMessageIter *i, const char *property, void *data) {
124 state = unit_active_state_to_string(unit_active_state(u));
126 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
132 static int bus_unit_append_sub_state(DBusMessageIter *i, const char *property, void *data) {
140 state = unit_sub_state_to_string(u);
142 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
148 static int bus_unit_append_file_state(DBusMessageIter *i, const char *property, void *data) {
156 state = strempty(unit_file_state_to_string(unit_get_unit_file_state(u)));
158 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
164 static int bus_unit_append_can_start(DBusMessageIter *i, const char *property, void *data) {
172 b = unit_can_start(u) &&
173 !u->refuse_manual_start;
175 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
181 static int bus_unit_append_can_stop(DBusMessageIter *i, const char *property, void *data) {
189 /* On the lower levels we assume that every unit we can start
190 * we can also stop */
192 b = unit_can_start(u) &&
193 !u->refuse_manual_stop;
195 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
201 static int bus_unit_append_can_reload(DBusMessageIter *i, const char *property, void *data) {
209 b = unit_can_reload(u);
211 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
217 static int bus_unit_append_can_isolate(DBusMessageIter *i, const char *property, void *data) {
225 b = unit_can_isolate(u) &&
226 !u->refuse_manual_start;
228 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
234 static int bus_unit_append_job(DBusMessageIter *i, const char *property, void *data) {
243 if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
248 if (!(p = job_dbus_path(u->job)))
251 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &u->job->id) ||
252 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p)) {
259 /* No job, so let's fill in some placeholder
260 * data. Since we need to fill in a valid path we
261 * simple point to ourselves. */
263 if (!(p = unit_dbus_path(u)))
266 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &id) ||
267 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p)) {
275 if (!dbus_message_iter_close_container(i, &sub))
281 static int bus_unit_append_default_cgroup(DBusMessageIter *i, const char *property, void *data) {
291 if ((cgb = unit_get_default_cgroup(u))) {
292 if (!(t = cgroup_bonding_to_string(cgb)))
297 success = dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t);
302 return success ? 0 : -ENOMEM;
305 static int bus_unit_append_cgroups(DBusMessageIter *i, const char *property, void *data) {
310 if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub))
313 LIST_FOREACH(by_unit, cgb, u->cgroup_bondings) {
317 if (!(t = cgroup_bonding_to_string(cgb)))
320 success = dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &t);
327 if (!dbus_message_iter_close_container(i, &sub))
333 static int bus_unit_append_cgroup_attrs(DBusMessageIter *i, const char *property, void *data) {
336 DBusMessageIter sub, sub2;
338 if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(sss)", &sub))
341 LIST_FOREACH(by_unit, a, u->cgroup_attributes) {
346 a->map_callback(a->controller, a->name, a->value, &v);
349 dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) &&
350 dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &a->controller) &&
351 dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &a->name) &&
352 dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, v ? &v : &a->value) &&
353 dbus_message_iter_close_container(&sub, &sub2);
361 if (!dbus_message_iter_close_container(i, &sub))
367 static int bus_unit_append_need_daemon_reload(DBusMessageIter *i, const char *property, void *data) {
375 b = unit_need_daemon_reload(u);
377 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
383 static int bus_unit_append_load_error(DBusMessageIter *i, const char *property, void *data) {
385 const char *name, *message;
392 if (u->load_error != 0) {
393 name = bus_errno_to_dbus(u->load_error);
394 message = strempty(strerror(-u->load_error));
398 if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub) ||
399 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &name) ||
400 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &message) ||
401 !dbus_message_iter_close_container(i, &sub))
407 static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *connection, DBusMessage *message) {
408 DBusMessage *reply = NULL;
409 Manager *m = u->manager;
411 JobType job_type = _JOB_TYPE_INVALID;
413 bool reload_if_possible = false;
415 dbus_error_init(&error);
417 if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Start"))
418 job_type = JOB_START;
419 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Stop"))
421 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Reload"))
422 job_type = JOB_RELOAD;
423 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Restart"))
424 job_type = JOB_RESTART;
425 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "TryRestart"))
426 job_type = JOB_TRY_RESTART;
427 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ReloadOrRestart")) {
428 reload_if_possible = true;
429 job_type = JOB_RESTART;
430 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ReloadOrTryRestart")) {
431 reload_if_possible = true;
432 job_type = JOB_TRY_RESTART;
433 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Kill")) {
434 const char *swho, *smode;
440 if (!dbus_message_get_args(
443 DBUS_TYPE_STRING, &swho,
444 DBUS_TYPE_STRING, &smode,
445 DBUS_TYPE_INT32, &signo,
447 return bus_send_error_reply(connection, message, &error, -EINVAL);
452 who = kill_who_from_string(swho);
454 return bus_send_error_reply(connection, message, &error, -EINVAL);
458 mode = KILL_CONTROL_GROUP;
460 mode = kill_mode_from_string(smode);
462 return bus_send_error_reply(connection, message, &error, -EINVAL);
465 if (signo <= 0 || signo >= _NSIG)
466 return bus_send_error_reply(connection, message, &error, -EINVAL);
468 if ((r = unit_kill(u, who, mode, signo, &error)) < 0)
469 return bus_send_error_reply(connection, message, &error, r);
471 if (!(reply = dbus_message_new_method_return(message)))
474 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ResetFailed")) {
476 unit_reset_failed(u);
478 if (!(reply = dbus_message_new_method_return(message)))
481 } else if (UNIT_VTABLE(u)->bus_message_handler)
482 return UNIT_VTABLE(u)->bus_message_handler(u, connection, message);
484 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
486 if (job_type != _JOB_TYPE_INVALID) {
492 if ((job_type == JOB_START && u->refuse_manual_start) ||
493 (job_type == JOB_STOP && u->refuse_manual_stop) ||
494 ((job_type == JOB_RESTART || job_type == JOB_TRY_RESTART) &&
495 (u->refuse_manual_start || u->refuse_manual_stop))) {
496 dbus_set_error(&error, BUS_ERROR_ONLY_BY_DEPENDENCY, "Operation refused, may be requested by dependency only.");
497 return bus_send_error_reply(connection, message, &error, -EPERM);
500 if (!dbus_message_get_args(
503 DBUS_TYPE_STRING, &smode,
505 return bus_send_error_reply(connection, message, &error, -EINVAL);
507 if (reload_if_possible && unit_can_reload(u)) {
508 if (job_type == JOB_RESTART)
509 job_type = JOB_RELOAD_OR_START;
510 else if (job_type == JOB_TRY_RESTART)
511 job_type = JOB_RELOAD;
514 if ((mode = job_mode_from_string(smode)) == _JOB_MODE_INVALID) {
515 dbus_set_error(&error, BUS_ERROR_INVALID_JOB_MODE, "Job mode %s is invalid.", smode);
516 return bus_send_error_reply(connection, message, &error, -EINVAL);
519 if ((r = manager_add_job(m, job_type, u, mode, true, &error, &j)) < 0)
520 return bus_send_error_reply(connection, message, &error, r);
522 if (!(reply = dbus_message_new_method_return(message)))
525 if (!(path = job_dbus_path(j)))
528 if (!dbus_message_append_args(
530 DBUS_TYPE_OBJECT_PATH, &path,
536 if (!dbus_connection_send(connection, reply, NULL))
539 dbus_message_unref(reply);
544 return DBUS_HANDLER_RESULT_HANDLED;
550 dbus_message_unref(reply);
552 dbus_error_free(&error);
554 return DBUS_HANDLER_RESULT_NEED_MEMORY;
557 static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DBusMessage *message, void *data) {
561 DBusMessage *reply = NULL;
568 dbus_error_init(&error);
570 if (streq(dbus_message_get_path(message), "/org/freedesktop/systemd1/unit")) {
571 /* Be nice to gdbus and return introspection data for our mid-level paths */
573 if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
574 char *introspection = NULL;
580 if (!(reply = dbus_message_new_method_return(message)))
583 /* We roll our own introspection code here, instead of
584 * relying on bus_default_message_handler() because we
585 * need to generate our introspection string
588 if (!(f = open_memstream(&introspection, &size)))
591 fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
594 fputs(BUS_INTROSPECTABLE_INTERFACE, f);
595 fputs(BUS_PEER_INTERFACE, f);
597 HASHMAP_FOREACH_KEY(u, k, m->units, i) {
603 if (!(p = bus_path_escape(k))) {
609 fprintf(f, "<node name=\"%s\"/>", p);
613 fputs("</node>\n", f);
626 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
633 if (!dbus_connection_send(connection, reply, NULL))
636 dbus_message_unref(reply);
638 return DBUS_HANDLER_RESULT_HANDLED;
641 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
644 r = manager_load_unit_from_dbus_path(m, dbus_message_get_path(message), &error, &u);
649 return bus_send_error_reply(connection, message, &error, r);
652 return bus_unit_message_dispatch(u, connection, message);
656 dbus_message_unref(reply);
658 dbus_error_free(&error);
660 return DBUS_HANDLER_RESULT_NEED_MEMORY;
663 const DBusObjectPathVTable bus_unit_vtable = {
664 .message_function = bus_unit_message_handler
667 void bus_unit_send_change_signal(Unit *u) {
669 DBusMessage *m = NULL;
673 if (u->in_dbus_queue) {
674 LIST_REMOVE(Unit, dbus_queue, u->manager->dbus_unit_queue, u);
675 u->in_dbus_queue = false;
681 if (!bus_has_subscriber(u->manager)) {
682 u->sent_dbus_new_signal = true;
686 if (!(p = unit_dbus_path(u)))
689 if (u->sent_dbus_new_signal) {
690 /* Send a properties changed signal. First for the
691 * specific type, then for the generic unit. The
692 * clients may rely on this order to get atomic
693 * behaviour if needed. */
695 if (UNIT_VTABLE(u)->bus_invalidating_properties) {
697 if (!(m = bus_properties_changed_new(p,
698 UNIT_VTABLE(u)->bus_interface,
699 UNIT_VTABLE(u)->bus_invalidating_properties)))
702 if (bus_broadcast(u->manager, m) < 0)
705 dbus_message_unref(m);
708 if (!(m = bus_properties_changed_new(p, "org.freedesktop.systemd1.Unit", INVALIDATING_PROPERTIES)))
712 /* Send a new signal */
714 if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "UnitNew")))
717 if (!dbus_message_append_args(m,
718 DBUS_TYPE_STRING, &u->id,
719 DBUS_TYPE_OBJECT_PATH, &p,
724 if (bus_broadcast(u->manager, m) < 0)
728 dbus_message_unref(m);
730 u->sent_dbus_new_signal = true;
738 dbus_message_unref(m);
740 log_error("Failed to allocate unit change/new signal.");
743 void bus_unit_send_removed_signal(Unit *u) {
745 DBusMessage *m = NULL;
749 if (!bus_has_subscriber(u->manager))
752 if (!u->sent_dbus_new_signal)
753 bus_unit_send_change_signal(u);
758 if (!(p = unit_dbus_path(u)))
761 if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "UnitRemoved")))
764 if (!dbus_message_append_args(m,
765 DBUS_TYPE_STRING, &u->id,
766 DBUS_TYPE_OBJECT_PATH, &p,
770 if (bus_broadcast(u->manager, m) < 0)
774 dbus_message_unref(m);
782 dbus_message_unref(m);
784 log_error("Failed to allocate unit remove signal.");
787 const BusProperty bus_unit_properties[] = {
788 { "Id", bus_property_append_string, "s", offsetof(Unit, id), true },
789 { "Names", bus_unit_append_names, "as", 0 },
790 { "Following", bus_unit_append_following, "s", 0 },
791 { "Requires", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRES]), true },
792 { "RequiresOverridable", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRES_OVERRIDABLE]), true },
793 { "Requisite", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUISITE]), true },
794 { "RequisiteOverridable", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUISITE_OVERRIDABLE]), true },
795 { "Wants", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_WANTS]), true },
796 { "BindTo", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BIND_TO]), true },
797 { "RequiredBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY]), true },
798 { "RequiredByOverridable",bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY_OVERRIDABLE]), true },
799 { "WantedBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_WANTED_BY]), true },
800 { "BoundBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BOUND_BY]), true },
801 { "Conflicts", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONFLICTS]), true },
802 { "ConflictedBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONFLICTED_BY]), true },
803 { "Before", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BEFORE]), true },
804 { "After", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_AFTER]), true },
805 { "OnFailure", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_ON_FAILURE]), true },
806 { "Triggers", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_TRIGGERS]), true },
807 { "TriggeredBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_TRIGGERED_BY]), true },
808 { "PropagateReloadTo", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_PROPAGATE_RELOAD_TO]), true },
809 { "PropagateReloadFrom", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_PROPAGATE_RELOAD_FROM]), true },
810 { "RequiresMountsFor", bus_property_append_strv, "as", offsetof(Unit, requires_mounts_for), true },
811 { "Documentation", bus_property_append_strv, "as", offsetof(Unit, documentation), true },
812 { "Description", bus_unit_append_description, "s", 0 },
813 { "LoadState", bus_unit_append_load_state, "s", offsetof(Unit, load_state) },
814 { "ActiveState", bus_unit_append_active_state, "s", 0 },
815 { "SubState", bus_unit_append_sub_state, "s", 0 },
816 { "FragmentPath", bus_property_append_string, "s", offsetof(Unit, fragment_path), true },
817 { "UnitFileState", bus_unit_append_file_state, "s", 0 },
818 { "InactiveExitTimestamp",bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.realtime) },
819 { "InactiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.monotonic) },
820 { "ActiveEnterTimestamp", bus_property_append_usec, "t", offsetof(Unit, active_enter_timestamp.realtime) },
821 { "ActiveEnterTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, active_enter_timestamp.monotonic) },
822 { "ActiveExitTimestamp", bus_property_append_usec, "t", offsetof(Unit, active_exit_timestamp.realtime) },
823 { "ActiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, active_exit_timestamp.monotonic) },
824 { "InactiveEnterTimestamp", bus_property_append_usec, "t", offsetof(Unit, inactive_enter_timestamp.realtime) },
825 { "InactiveEnterTimestampMonotonic",bus_property_append_usec, "t", offsetof(Unit, inactive_enter_timestamp.monotonic) },
826 { "CanStart", bus_unit_append_can_start, "b", 0 },
827 { "CanStop", bus_unit_append_can_stop, "b", 0 },
828 { "CanReload", bus_unit_append_can_reload, "b", 0 },
829 { "CanIsolate", bus_unit_append_can_isolate, "b", 0 },
830 { "Job", bus_unit_append_job, "(uo)", 0 },
831 { "StopWhenUnneeded", bus_property_append_bool, "b", offsetof(Unit, stop_when_unneeded) },
832 { "RefuseManualStart", bus_property_append_bool, "b", offsetof(Unit, refuse_manual_start) },
833 { "RefuseManualStop", bus_property_append_bool, "b", offsetof(Unit, refuse_manual_stop) },
834 { "AllowIsolate", bus_property_append_bool, "b", offsetof(Unit, allow_isolate) },
835 { "DefaultDependencies", bus_property_append_bool, "b", offsetof(Unit, default_dependencies) },
836 { "OnFailureIsolate", bus_property_append_bool, "b", offsetof(Unit, on_failure_isolate) },
837 { "IgnoreOnIsolate", bus_property_append_bool, "b", offsetof(Unit, ignore_on_isolate) },
838 { "IgnoreOnSnapshot", bus_property_append_bool, "b", offsetof(Unit, ignore_on_snapshot) },
839 { "DefaultControlGroup", bus_unit_append_default_cgroup, "s", 0 },
840 { "ControlGroup", bus_unit_append_cgroups, "as", 0 },
841 { "ControlGroupAttributes", bus_unit_append_cgroup_attrs,"a(sss)", 0 },
842 { "NeedDaemonReload", bus_unit_append_need_daemon_reload, "b", 0 },
843 { "JobTimeoutUSec", bus_property_append_usec, "t", offsetof(Unit, job_timeout) },
844 { "ConditionTimestamp", bus_property_append_usec, "t", offsetof(Unit, condition_timestamp.realtime) },
845 { "ConditionTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, condition_timestamp.monotonic) },
846 { "ConditionResult", bus_property_append_bool, "b", offsetof(Unit, condition_result) },
847 { "LoadError", bus_unit_append_load_error, "(ss)", 0 },