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) {
567 if (streq(dbus_message_get_path(message), "/org/freedesktop/systemd1/unit")) {
568 /* Be nice to gdbus and return introspection data for our mid-level paths */
570 if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
571 char *introspection = NULL;
577 if (!(reply = dbus_message_new_method_return(message)))
580 /* We roll our own introspection code here, instead of
581 * relying on bus_default_message_handler() because we
582 * need to generate our introspection string
585 if (!(f = open_memstream(&introspection, &size)))
588 fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
591 fputs(BUS_INTROSPECTABLE_INTERFACE, f);
592 fputs(BUS_PEER_INTERFACE, f);
594 HASHMAP_FOREACH_KEY(u, k, m->units, i) {
600 if (!(p = bus_path_escape(k))) {
606 fprintf(f, "<node name=\"%s\"/>", p);
610 fputs("</node>\n", f);
623 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
630 if (!dbus_connection_send(connection, reply, NULL))
633 dbus_message_unref(reply);
635 return DBUS_HANDLER_RESULT_HANDLED;
638 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
641 if ((r = manager_get_unit_from_dbus_path(m, dbus_message_get_path(message), &u)) < 0) {
644 return DBUS_HANDLER_RESULT_NEED_MEMORY;
650 dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown unit");
651 return bus_send_error_reply(connection, message, &e, r);
654 return bus_send_error_reply(connection, message, NULL, r);
657 return bus_unit_message_dispatch(u, connection, message);
661 dbus_message_unref(reply);
663 return DBUS_HANDLER_RESULT_NEED_MEMORY;
666 const DBusObjectPathVTable bus_unit_vtable = {
667 .message_function = bus_unit_message_handler
670 void bus_unit_send_change_signal(Unit *u) {
672 DBusMessage *m = NULL;
676 if (u->in_dbus_queue) {
677 LIST_REMOVE(Unit, dbus_queue, u->manager->dbus_unit_queue, u);
678 u->in_dbus_queue = false;
684 if (!bus_has_subscriber(u->manager)) {
685 u->sent_dbus_new_signal = true;
689 if (!(p = unit_dbus_path(u)))
692 if (u->sent_dbus_new_signal) {
693 /* Send a properties changed signal. First for the
694 * specific type, then for the generic unit. The
695 * clients may rely on this order to get atomic
696 * behaviour if needed. */
698 if (UNIT_VTABLE(u)->bus_invalidating_properties) {
700 if (!(m = bus_properties_changed_new(p,
701 UNIT_VTABLE(u)->bus_interface,
702 UNIT_VTABLE(u)->bus_invalidating_properties)))
705 if (bus_broadcast(u->manager, m) < 0)
708 dbus_message_unref(m);
711 if (!(m = bus_properties_changed_new(p, "org.freedesktop.systemd1.Unit", INVALIDATING_PROPERTIES)))
715 /* Send a new signal */
717 if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "UnitNew")))
720 if (!dbus_message_append_args(m,
721 DBUS_TYPE_STRING, &u->id,
722 DBUS_TYPE_OBJECT_PATH, &p,
727 if (bus_broadcast(u->manager, m) < 0)
731 dbus_message_unref(m);
733 u->sent_dbus_new_signal = true;
741 dbus_message_unref(m);
743 log_error("Failed to allocate unit change/new signal.");
746 void bus_unit_send_removed_signal(Unit *u) {
748 DBusMessage *m = NULL;
752 if (!bus_has_subscriber(u->manager))
755 if (!u->sent_dbus_new_signal)
756 bus_unit_send_change_signal(u);
761 if (!(p = unit_dbus_path(u)))
764 if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "UnitRemoved")))
767 if (!dbus_message_append_args(m,
768 DBUS_TYPE_STRING, &u->id,
769 DBUS_TYPE_OBJECT_PATH, &p,
773 if (bus_broadcast(u->manager, m) < 0)
777 dbus_message_unref(m);
785 dbus_message_unref(m);
787 log_error("Failed to allocate unit remove signal.");
790 const BusProperty bus_unit_properties[] = {
791 { "Id", bus_property_append_string, "s", offsetof(Unit, id), true },
792 { "Names", bus_unit_append_names, "as", 0 },
793 { "Following", bus_unit_append_following, "s", 0 },
794 { "Requires", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRES]), true },
795 { "RequiresOverridable", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRES_OVERRIDABLE]), true },
796 { "Requisite", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUISITE]), true },
797 { "RequisiteOverridable", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUISITE_OVERRIDABLE]), true },
798 { "Wants", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_WANTS]), true },
799 { "BindTo", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BIND_TO]), true },
800 { "RequiredBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY]), true },
801 { "RequiredByOverridable",bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY_OVERRIDABLE]), true },
802 { "WantedBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_WANTED_BY]), true },
803 { "BoundBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BOUND_BY]), true },
804 { "Conflicts", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONFLICTS]), true },
805 { "ConflictedBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONFLICTED_BY]), true },
806 { "Before", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BEFORE]), true },
807 { "After", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_AFTER]), true },
808 { "OnFailure", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_ON_FAILURE]), true },
809 { "Triggers", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_TRIGGERS]), true },
810 { "TriggeredBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_TRIGGERED_BY]), true },
811 { "PropagateReloadTo", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_PROPAGATE_RELOAD_TO]), true },
812 { "PropagateReloadFrom", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_PROPAGATE_RELOAD_FROM]), true },
813 { "Description", bus_unit_append_description, "s", 0 },
814 { "LoadState", bus_unit_append_load_state, "s", offsetof(Unit, load_state) },
815 { "ActiveState", bus_unit_append_active_state, "s", 0 },
816 { "SubState", bus_unit_append_sub_state, "s", 0 },
817 { "FragmentPath", bus_property_append_string, "s", offsetof(Unit, fragment_path), true },
818 { "UnitFileState", bus_unit_append_file_state, "s", 0 },
819 { "InactiveExitTimestamp",bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.realtime) },
820 { "InactiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.monotonic) },
821 { "ActiveEnterTimestamp", bus_property_append_usec, "t", offsetof(Unit, active_enter_timestamp.realtime) },
822 { "ActiveEnterTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, active_enter_timestamp.monotonic) },
823 { "ActiveExitTimestamp", bus_property_append_usec, "t", offsetof(Unit, active_exit_timestamp.realtime) },
824 { "ActiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, active_exit_timestamp.monotonic) },
825 { "InactiveEnterTimestamp", bus_property_append_usec, "t", offsetof(Unit, inactive_enter_timestamp.realtime) },
826 { "InactiveEnterTimestampMonotonic",bus_property_append_usec, "t", offsetof(Unit, inactive_enter_timestamp.monotonic) },
827 { "CanStart", bus_unit_append_can_start, "b", 0 },
828 { "CanStop", bus_unit_append_can_stop, "b", 0 },
829 { "CanReload", bus_unit_append_can_reload, "b", 0 },
830 { "CanIsolate", bus_unit_append_can_isolate, "b", 0 },
831 { "Job", bus_unit_append_job, "(uo)", 0 },
832 { "StopWhenUnneeded", bus_property_append_bool, "b", offsetof(Unit, stop_when_unneeded) },
833 { "RefuseManualStart", bus_property_append_bool, "b", offsetof(Unit, refuse_manual_start) },
834 { "RefuseManualStop", bus_property_append_bool, "b", offsetof(Unit, refuse_manual_stop) },
835 { "AllowIsolate", bus_property_append_bool, "b", offsetof(Unit, allow_isolate) },
836 { "DefaultDependencies", bus_property_append_bool, "b", offsetof(Unit, default_dependencies) },
837 { "OnFailureIsolate", bus_property_append_bool, "b", offsetof(Unit, on_failure_isolate) },
838 { "IgnoreOnIsolate", bus_property_append_bool, "b", offsetof(Unit, ignore_on_isolate) },
839 { "IgnoreOnSnapshot", bus_property_append_bool, "b", offsetof(Unit, ignore_on_snapshot) },
840 { "DefaultControlGroup", bus_unit_append_default_cgroup, "s", 0 },
841 { "ControlGroup", bus_unit_append_cgroups, "as", 0 },
842 { "ControlGroupAttributes", bus_unit_append_cgroup_attrs,"a(sss)", 0 },
843 { "NeedDaemonReload", bus_unit_append_need_daemon_reload, "b", 0 },
844 { "JobTimeoutUSec", bus_property_append_usec, "t", offsetof(Unit, job_timeout) },
845 { "ConditionTimestamp", bus_property_append_usec, "t", offsetof(Unit, condition_timestamp.realtime) },
846 { "ConditionTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, condition_timestamp.monotonic) },
847 { "ConditionResult", bus_property_append_bool, "b", offsetof(Unit, condition_result) },
848 { "LoadError", bus_unit_append_load_error, "(ss)", 0 },