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_need_daemon_reload(DBusMessageIter
*i
, const char *property
, void *data
) {
306 b
= unit_need_daemon_reload(u
);
308 if (!dbus_message_iter_append_basic(i
, DBUS_TYPE_BOOLEAN
, &b
))
314 static int bus_unit_append_load_error(DBusMessageIter
*i
, const char *property
, void *data
) {
316 const char *name
, *message
;
323 if (u
->load_error
!= 0) {
324 name
= bus_errno_to_dbus(u
->load_error
);
325 message
= strempty(strerror(-u
->load_error
));
329 if (!dbus_message_iter_open_container(i
, DBUS_TYPE_STRUCT
, NULL
, &sub
) ||
330 !dbus_message_iter_append_basic(&sub
, DBUS_TYPE_STRING
, &name
) ||
331 !dbus_message_iter_append_basic(&sub
, DBUS_TYPE_STRING
, &message
) ||
332 !dbus_message_iter_close_container(i
, &sub
))
338 static DBusHandlerResult
bus_unit_message_dispatch(Unit
*u
, DBusConnection
*connection
, DBusMessage
*message
) {
339 _cleanup_dbus_message_unref_ DBusMessage
*reply
= NULL
;
341 JobType job_type
= _JOB_TYPE_INVALID
;
342 bool reload_if_possible
= false;
345 dbus_error_init(&error
);
347 if (dbus_message_is_method_call(message
, "org.freedesktop.systemd1.Unit", "Start"))
348 job_type
= JOB_START
;
349 else if (dbus_message_is_method_call(message
, "org.freedesktop.systemd1.Unit", "Stop"))
351 else if (dbus_message_is_method_call(message
, "org.freedesktop.systemd1.Unit", "Reload"))
352 job_type
= JOB_RELOAD
;
353 else if (dbus_message_is_method_call(message
, "org.freedesktop.systemd1.Unit", "Restart"))
354 job_type
= JOB_RESTART
;
355 else if (dbus_message_is_method_call(message
, "org.freedesktop.systemd1.Unit", "TryRestart"))
356 job_type
= JOB_TRY_RESTART
;
357 else if (dbus_message_is_method_call(message
, "org.freedesktop.systemd1.Unit", "ReloadOrRestart")) {
358 reload_if_possible
= true;
359 job_type
= JOB_RESTART
;
360 } else if (dbus_message_is_method_call(message
, "org.freedesktop.systemd1.Unit", "ReloadOrTryRestart")) {
361 reload_if_possible
= true;
362 job_type
= JOB_TRY_RESTART
;
363 } else if (dbus_message_is_method_call(message
, "org.freedesktop.systemd1.Unit", "Kill")) {
368 if (!dbus_message_get_args(
371 DBUS_TYPE_STRING
, &swho
,
372 DBUS_TYPE_INT32
, &signo
,
374 return bus_send_error_reply(connection
, message
, &error
, -EINVAL
);
379 who
= kill_who_from_string(swho
);
381 return bus_send_error_reply(connection
, message
, &error
, -EINVAL
);
384 if (signo
<= 0 || signo
>= _NSIG
)
385 return bus_send_error_reply(connection
, message
, &error
, -EINVAL
);
387 SELINUX_UNIT_ACCESS_CHECK(u
, connection
, message
, "stop");
389 r
= unit_kill(u
, who
, signo
, &error
);
391 return bus_send_error_reply(connection
, message
, &error
, r
);
393 reply
= dbus_message_new_method_return(message
);
397 } else if (dbus_message_is_method_call(message
, "org.freedesktop.systemd1.Unit", "ResetFailed")) {
399 SELINUX_UNIT_ACCESS_CHECK(u
, connection
, message
, "reload");
401 unit_reset_failed(u
);
403 reply
= dbus_message_new_method_return(message
);
406 } else if (dbus_message_is_method_call(message
, "org.freedesktop.systemd1.Unit", "SetProperties")) {
407 DBusMessageIter iter
;
410 if (!dbus_message_iter_init(message
, &iter
))
413 if (bus_iter_get_basic_and_next(&iter
, DBUS_TYPE_BOOLEAN
, &runtime
, true) < 0)
414 return bus_send_error_reply(connection
, message
, NULL
, -EINVAL
);
416 SELINUX_UNIT_ACCESS_CHECK(u
, connection
, message
, "start");
418 r
= bus_unit_set_properties(u
, &iter
, runtime
? UNIT_RUNTIME
: UNIT_PERSISTENT
, true, &error
);
420 return bus_send_error_reply(connection
, message
, &error
, r
);
422 reply
= dbus_message_new_method_return(message
);
426 } else if (UNIT_VTABLE(u
)->bus_message_handler
)
427 return UNIT_VTABLE(u
)->bus_message_handler(u
, connection
, message
);
429 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
431 if (job_type
!= _JOB_TYPE_INVALID
) {
435 if (!dbus_message_get_args(
438 DBUS_TYPE_STRING
, &smode
,
440 return bus_send_error_reply(connection
, message
, &error
, -EINVAL
);
442 mode
= job_mode_from_string(smode
);
444 dbus_set_error(&error
, BUS_ERROR_INVALID_JOB_MODE
, "Job mode %s is invalid.", smode
);
445 return bus_send_error_reply(connection
, message
, &error
, -EINVAL
);
448 return bus_unit_queue_job(connection
, message
, u
, job_type
, mode
, reload_if_possible
);
452 if (!bus_maybe_send_reply(connection
, message
, reply
))
455 return DBUS_HANDLER_RESULT_HANDLED
;
458 dbus_error_free(&error
);
459 return DBUS_HANDLER_RESULT_NEED_MEMORY
;
462 static DBusHandlerResult
bus_unit_message_handler(DBusConnection
*connection
, DBusMessage
*message
, void *data
) {
466 _cleanup_dbus_message_unref_ DBusMessage
*reply
= NULL
;
473 dbus_error_init(&error
);
475 if (streq(dbus_message_get_path(message
), "/org/freedesktop/systemd1/unit")) {
476 /* Be nice to gdbus and return introspection data for our mid-level paths */
478 SELINUX_ACCESS_CHECK(connection
, message
, "status");
480 if (dbus_message_is_method_call(message
, "org.freedesktop.DBus.Introspectable", "Introspect")) {
481 char *introspection
= NULL
;
487 reply
= dbus_message_new_method_return(message
);
491 /* We roll our own introspection code here, instead of
492 * relying on bus_default_message_handler() because we
493 * need to generate our introspection string
496 f
= open_memstream(&introspection
, &size
);
500 fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
503 fputs(BUS_INTROSPECTABLE_INTERFACE
, f
);
504 fputs(BUS_PEER_INTERFACE
, f
);
506 HASHMAP_FOREACH_KEY(u
, k
, m
->units
, i
) {
512 p
= bus_path_escape(k
);
519 fprintf(f
, "<node name=\"%s\"/>", p
);
523 fputs("</node>\n", f
);
536 if (!dbus_message_append_args(reply
, DBUS_TYPE_STRING
, &introspection
, DBUS_TYPE_INVALID
)) {
543 if (!bus_maybe_send_reply(connection
, message
, reply
))
546 return DBUS_HANDLER_RESULT_HANDLED
;
549 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
552 r
= manager_load_unit_from_dbus_path(m
, dbus_message_get_path(message
), &error
, &u
);
556 return bus_send_error_reply(connection
, message
, &error
, r
);
558 return bus_unit_message_dispatch(u
, connection
, message
);
561 dbus_error_free(&error
);
563 return DBUS_HANDLER_RESULT_NEED_MEMORY
;
566 const DBusObjectPathVTable bus_unit_vtable
= {
567 .message_function
= bus_unit_message_handler
570 void bus_unit_send_change_signal(Unit
*u
) {
571 _cleanup_free_
char *p
= NULL
;
572 _cleanup_dbus_message_unref_ DBusMessage
*m
= NULL
;
576 if (u
->in_dbus_queue
) {
577 LIST_REMOVE(Unit
, dbus_queue
, u
->manager
->dbus_unit_queue
, u
);
578 u
->in_dbus_queue
= false;
584 if (!bus_has_subscriber(u
->manager
)) {
585 u
->sent_dbus_new_signal
= true;
589 p
= unit_dbus_path(u
);
593 if (u
->sent_dbus_new_signal
) {
594 /* Send a properties changed signal. First for the
595 * specific type, then for the generic unit. The
596 * clients may rely on this order to get atomic
597 * behavior if needed. */
599 if (UNIT_VTABLE(u
)->bus_invalidating_properties
) {
601 m
= bus_properties_changed_new(p
,
602 UNIT_VTABLE(u
)->bus_interface
,
603 UNIT_VTABLE(u
)->bus_invalidating_properties
);
607 if (bus_broadcast(u
->manager
, m
) < 0)
610 dbus_message_unref(m
);
613 m
= bus_properties_changed_new(p
, "org.freedesktop.systemd1.Unit",
614 INVALIDATING_PROPERTIES
);
619 /* Send a new signal */
621 m
= dbus_message_new_signal("/org/freedesktop/systemd1",
622 "org.freedesktop.systemd1.Manager",
627 if (!dbus_message_append_args(m
,
628 DBUS_TYPE_STRING
, &u
->id
,
629 DBUS_TYPE_OBJECT_PATH
, &p
,
634 if (bus_broadcast(u
->manager
, m
) < 0)
637 u
->sent_dbus_new_signal
= true;
645 void bus_unit_send_removed_signal(Unit
*u
) {
646 _cleanup_free_
char *p
= NULL
;
647 _cleanup_dbus_message_unref_ DBusMessage
*m
= NULL
;
651 if (!bus_has_subscriber(u
->manager
))
654 if (!u
->sent_dbus_new_signal
)
655 bus_unit_send_change_signal(u
);
660 p
= unit_dbus_path(u
);
664 m
= dbus_message_new_signal("/org/freedesktop/systemd1",
665 "org.freedesktop.systemd1.Manager",
670 if (!dbus_message_append_args(m
,
671 DBUS_TYPE_STRING
, &u
->id
,
672 DBUS_TYPE_OBJECT_PATH
, &p
,
676 if (bus_broadcast(u
->manager
, m
) < 0)
685 DBusHandlerResult
bus_unit_queue_job(
686 DBusConnection
*connection
,
687 DBusMessage
*message
,
691 bool reload_if_possible
) {
693 _cleanup_dbus_message_unref_ DBusMessage
*reply
= NULL
;
694 _cleanup_free_
char *path
= NULL
;
703 assert(type
>= 0 && type
< _JOB_TYPE_MAX
);
704 assert(mode
>= 0 && mode
< _JOB_MODE_MAX
);
706 dbus_error_init(&error
);
708 if (reload_if_possible
&& unit_can_reload(u
)) {
709 if (type
== JOB_RESTART
)
710 type
= JOB_RELOAD_OR_START
;
711 else if (type
== JOB_TRY_RESTART
)
715 SELINUX_UNIT_ACCESS_CHECK(u
, connection
, message
,
716 (type
== JOB_START
|| type
== JOB_RESTART
|| type
== JOB_TRY_RESTART
) ? "start" :
717 type
== JOB_STOP
? "stop" : "reload");
719 if (type
== JOB_STOP
&& (u
->load_state
== UNIT_NOT_FOUND
|| u
->load_state
== UNIT_ERROR
) && unit_active_state(u
) == UNIT_INACTIVE
) {
720 dbus_set_error(&error
, BUS_ERROR_NO_SUCH_UNIT
, "Unit %s not loaded.", u
->id
);
721 return bus_send_error_reply(connection
, message
, &error
, -EPERM
);
724 if ((type
== JOB_START
&& u
->refuse_manual_start
) ||
725 (type
== JOB_STOP
&& u
->refuse_manual_stop
) ||
726 ((type
== JOB_RESTART
|| type
== JOB_TRY_RESTART
) && (u
->refuse_manual_start
|| u
->refuse_manual_stop
))) {
727 dbus_set_error(&error
, BUS_ERROR_ONLY_BY_DEPENDENCY
,
728 "Operation refused, unit %s may be requested by dependency only.", u
->id
);
729 return bus_send_error_reply(connection
, message
, &error
, -EPERM
);
732 r
= manager_add_job(u
->manager
, type
, u
, mode
, true, &error
, &j
);
734 return bus_send_error_reply(connection
, message
, &error
, r
);
736 cl
= job_bus_client_new(connection
, bus_message_get_sender_with_fallback(message
));
740 LIST_PREPEND(JobBusClient
, client
, j
->bus_client_list
, cl
);
742 reply
= dbus_message_new_method_return(message
);
746 path
= job_dbus_path(j
);
750 if (!dbus_message_append_args(
752 DBUS_TYPE_OBJECT_PATH
, &path
,
756 if (!bus_maybe_send_reply(connection
, message
, reply
))
759 return DBUS_HANDLER_RESULT_HANDLED
;
762 dbus_error_free(&error
);
764 return DBUS_HANDLER_RESULT_NEED_MEMORY
;
767 static int bus_unit_set_transient_property(
771 UnitSetPropertiesMode mode
,
780 if (streq(name
, "Description")) {
781 const char *description
;
783 if (dbus_message_iter_get_arg_type(i
) != DBUS_TYPE_STRING
)
786 dbus_message_iter_get_basic(i
, &description
);
788 r
= unit_set_description(u
, description
);
794 } else if (streq(name
, "Slice") && unit_get_cgroup_context(u
)) {
798 if (dbus_message_iter_get_arg_type(i
) != DBUS_TYPE_STRING
)
801 dbus_message_iter_get_basic(i
, &s
);
803 r
= manager_load_unit(u
->manager
, s
, NULL
, error
, &slice
);
807 if (slice
->type
!= UNIT_SLICE
)
810 unit_ref_set(&u
->slice
, slice
);
817 int bus_unit_set_properties(
819 DBusMessageIter
*iter
,
820 UnitSetPropertiesMode mode
,
824 bool for_real
= false;
833 mode
&= UNIT_RUNTIME
;
835 /* We iterate through the array twice. First run we just check
836 * if all passed data is valid, second run actually applies
837 * it. This is to implement transaction-like behaviour without
838 * actually providing full transactions. */
840 if (dbus_message_iter_get_arg_type(iter
) != DBUS_TYPE_ARRAY
||
841 dbus_message_iter_get_element_type(iter
) != DBUS_TYPE_STRUCT
)
844 dbus_message_iter_recurse(iter
, &sub
);
846 DBusMessageIter sub2
, sub3
;
849 if (dbus_message_iter_get_arg_type(&sub
) == DBUS_TYPE_INVALID
) {
854 /* Reached EOF. Let's try again, and this time for realz... */
855 dbus_message_iter_recurse(iter
, &sub
);
860 if (dbus_message_iter_get_arg_type(&sub
) != DBUS_TYPE_STRUCT
)
863 dbus_message_iter_recurse(&sub
, &sub2
);
865 if (bus_iter_get_basic_and_next(&sub2
, DBUS_TYPE_STRING
, &name
, true) < 0 ||
866 dbus_message_iter_get_arg_type(&sub2
) != DBUS_TYPE_VARIANT
)
869 if (!UNIT_VTABLE(u
)->bus_set_property
) {
870 dbus_set_error(error
, DBUS_ERROR_PROPERTY_READ_ONLY
, "Objects of this type do not support setting properties.");
874 dbus_message_iter_recurse(&sub2
, &sub3
);
875 r
= UNIT_VTABLE(u
)->bus_set_property(u
, name
, &sub3
, for_real
? mode
: UNIT_CHECK
, error
);
876 if (r
== 0 && u
->transient
&& u
->load_state
== UNIT_STUB
)
877 r
= bus_unit_set_transient_property(u
, name
, &sub3
, for_real
? mode
: UNIT_CHECK
, error
);
881 dbus_set_error(error
, DBUS_ERROR_PROPERTY_READ_ONLY
, "Cannot set property %s, or unknown property.", name
);
885 dbus_message_iter_next(&sub
);
890 if (commit
&& n
> 0 && UNIT_VTABLE(u
)->bus_commit_properties
)
891 UNIT_VTABLE(u
)->bus_commit_properties(u
);
896 const BusProperty bus_unit_properties
[] = {
897 { "Id", bus_property_append_string
, "s", offsetof(Unit
, id
), true },
898 { "Names", bus_unit_append_names
, "as", 0 },
899 { "Following", bus_unit_append_following
, "s", 0 },
900 { "Requires", bus_unit_append_dependencies
, "as", offsetof(Unit
, dependencies
[UNIT_REQUIRES
]), true },
901 { "RequiresOverridable", bus_unit_append_dependencies
, "as", offsetof(Unit
, dependencies
[UNIT_REQUIRES_OVERRIDABLE
]), true },
902 { "Requisite", bus_unit_append_dependencies
, "as", offsetof(Unit
, dependencies
[UNIT_REQUISITE
]), true },
903 { "RequisiteOverridable", bus_unit_append_dependencies
, "as", offsetof(Unit
, dependencies
[UNIT_REQUISITE_OVERRIDABLE
]), true },
904 { "Wants", bus_unit_append_dependencies
, "as", offsetof(Unit
, dependencies
[UNIT_WANTS
]), true },
905 { "BindsTo", bus_unit_append_dependencies
, "as", offsetof(Unit
, dependencies
[UNIT_BINDS_TO
]), true },
906 { "PartOf", bus_unit_append_dependencies
, "as", offsetof(Unit
, dependencies
[UNIT_PART_OF
]), true },
907 { "RequiredBy", bus_unit_append_dependencies
, "as", offsetof(Unit
, dependencies
[UNIT_REQUIRED_BY
]), true },
908 { "RequiredByOverridable",bus_unit_append_dependencies
, "as", offsetof(Unit
, dependencies
[UNIT_REQUIRED_BY_OVERRIDABLE
]), true },
909 { "WantedBy", bus_unit_append_dependencies
, "as", offsetof(Unit
, dependencies
[UNIT_WANTED_BY
]), true },
910 { "BoundBy", bus_unit_append_dependencies
, "as", offsetof(Unit
, dependencies
[UNIT_BOUND_BY
]), true },
911 { "ConsistsOf", bus_unit_append_dependencies
, "as", offsetof(Unit
, dependencies
[UNIT_CONSISTS_OF
]), true },
912 { "Conflicts", bus_unit_append_dependencies
, "as", offsetof(Unit
, dependencies
[UNIT_CONFLICTS
]), true },
913 { "ConflictedBy", bus_unit_append_dependencies
, "as", offsetof(Unit
, dependencies
[UNIT_CONFLICTED_BY
]), true },
914 { "Before", bus_unit_append_dependencies
, "as", offsetof(Unit
, dependencies
[UNIT_BEFORE
]), true },
915 { "After", bus_unit_append_dependencies
, "as", offsetof(Unit
, dependencies
[UNIT_AFTER
]), true },
916 { "OnFailure", bus_unit_append_dependencies
, "as", offsetof(Unit
, dependencies
[UNIT_ON_FAILURE
]), true },
917 { "Triggers", bus_unit_append_dependencies
, "as", offsetof(Unit
, dependencies
[UNIT_TRIGGERS
]), true },
918 { "TriggeredBy", bus_unit_append_dependencies
, "as", offsetof(Unit
, dependencies
[UNIT_TRIGGERED_BY
]), true },
919 { "PropagatesReloadTo", bus_unit_append_dependencies
, "as", offsetof(Unit
, dependencies
[UNIT_PROPAGATES_RELOAD_TO
]), true },
920 { "ReloadPropagatedFrom", bus_unit_append_dependencies
, "as", offsetof(Unit
, dependencies
[UNIT_RELOAD_PROPAGATED_FROM
]), true },
921 { "RequiresMountsFor", bus_property_append_strv
, "as", offsetof(Unit
, requires_mounts_for
), true },
922 { "Documentation", bus_property_append_strv
, "as", offsetof(Unit
, documentation
), true },
923 { "Description", bus_unit_append_description
, "s", 0 },
924 { "LoadState", bus_unit_append_load_state
, "s", offsetof(Unit
, load_state
) },
925 { "ActiveState", bus_unit_append_active_state
, "s", 0 },
926 { "SubState", bus_unit_append_sub_state
, "s", 0 },
927 { "FragmentPath", bus_property_append_string
, "s", offsetof(Unit
, fragment_path
), true },
928 { "SourcePath", bus_property_append_string
, "s", offsetof(Unit
, source_path
), true },
929 { "DropInPaths", bus_property_append_strv
, "as", offsetof(Unit
, dropin_paths
), true },
930 { "UnitFileState", bus_unit_append_file_state
, "s", 0 },
931 { "InactiveExitTimestamp",bus_property_append_usec
, "t", offsetof(Unit
, inactive_exit_timestamp
.realtime
) },
932 { "InactiveExitTimestampMonotonic", bus_property_append_usec
, "t", offsetof(Unit
, inactive_exit_timestamp
.monotonic
) },
933 { "ActiveEnterTimestamp", bus_property_append_usec
, "t", offsetof(Unit
, active_enter_timestamp
.realtime
) },
934 { "ActiveEnterTimestampMonotonic", bus_property_append_usec
, "t", offsetof(Unit
, active_enter_timestamp
.monotonic
) },
935 { "ActiveExitTimestamp", bus_property_append_usec
, "t", offsetof(Unit
, active_exit_timestamp
.realtime
) },
936 { "ActiveExitTimestampMonotonic", bus_property_append_usec
, "t", offsetof(Unit
, active_exit_timestamp
.monotonic
) },
937 { "InactiveEnterTimestamp", bus_property_append_usec
, "t", offsetof(Unit
, inactive_enter_timestamp
.realtime
) },
938 { "InactiveEnterTimestampMonotonic",bus_property_append_usec
, "t", offsetof(Unit
, inactive_enter_timestamp
.monotonic
) },
939 { "CanStart", bus_unit_append_can_start
, "b", 0 },
940 { "CanStop", bus_unit_append_can_stop
, "b", 0 },
941 { "CanReload", bus_unit_append_can_reload
, "b", 0 },
942 { "CanIsolate", bus_unit_append_can_isolate
, "b", 0 },
943 { "Job", bus_unit_append_job
, "(uo)", 0 },
944 { "StopWhenUnneeded", bus_property_append_bool
, "b", offsetof(Unit
, stop_when_unneeded
) },
945 { "RefuseManualStart", bus_property_append_bool
, "b", offsetof(Unit
, refuse_manual_start
) },
946 { "RefuseManualStop", bus_property_append_bool
, "b", offsetof(Unit
, refuse_manual_stop
) },
947 { "AllowIsolate", bus_property_append_bool
, "b", offsetof(Unit
, allow_isolate
) },
948 { "DefaultDependencies", bus_property_append_bool
, "b", offsetof(Unit
, default_dependencies
) },
949 { "OnFailureIsolate", bus_property_append_bool
, "b", offsetof(Unit
, on_failure_isolate
) },
950 { "IgnoreOnIsolate", bus_property_append_bool
, "b", offsetof(Unit
, ignore_on_isolate
) },
951 { "IgnoreOnSnapshot", bus_property_append_bool
, "b", offsetof(Unit
, ignore_on_snapshot
) },
952 { "NeedDaemonReload", bus_unit_append_need_daemon_reload
, "b", 0 },
953 { "JobTimeoutUSec", bus_property_append_usec
, "t", offsetof(Unit
, job_timeout
) },
954 { "ConditionTimestamp", bus_property_append_usec
, "t", offsetof(Unit
, condition_timestamp
.realtime
) },
955 { "ConditionTimestampMonotonic", bus_property_append_usec
, "t", offsetof(Unit
, condition_timestamp
.monotonic
) },
956 { "ConditionResult", bus_property_append_bool
, "b", offsetof(Unit
, condition_result
) },
957 { "LoadError", bus_unit_append_load_error
, "(ss)", 0 },
958 { "Transient", bus_property_append_bool
, "b", offsetof(Unit
, transient
) },
962 const BusProperty bus_unit_cgroup_properties
[] = {
963 { "Slice", bus_unit_append_slice
, "s", 0 },
964 { "ControlGroup", bus_property_append_string
, "s", offsetof(Unit
, cgroup_path
), true },