diff --git a/man/org.freedesktop.systemd1.xml b/man/org.freedesktop.systemd1.xml
index f1f433bbac..8d16d6618d 100644
--- a/man/org.freedesktop.systemd1.xml
+++ b/man/org.freedesktop.systemd1.xml
@@ -413,6 +413,8 @@ node /org/freedesktop/systemd1 {
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly u NFailedJobs = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly at TransactionsWithOrderingCycle = [...];
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly d Progress = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly as Environment = ['...', ...];
@@ -1103,6 +1105,8 @@ node /org/freedesktop/systemd1 {
+
+
@@ -1809,6 +1813,9 @@ node /org/freedesktop/systemd1 {
NFailedJobs encodes how many jobs have ever failed in total.
+ TransactionsWithOrderingCycle encodes IDs of transactions that encountered
+ ordering cycle.
+
Progress encodes boot progress as a floating point value between 0.0 and
1.0. This value begins at 0.0 at early-boot and ends at 1.0 when boot is finished and is based on the
number of executed and queued jobs. After startup, this field is always 1.0 indicating a finished
@@ -12465,6 +12472,7 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
DefaultRestrictSUIDSGID,
RemoveSubgroupFromUnit(), and
KillUnitSubgroup() were added in version 258.
+ TransactionsWithOrderingCycle was added in version 259.Unit Objects
diff --git a/man/systemctl.xml b/man/systemctl.xml
index 8fb17e284d..a290e990e5 100644
--- a/man/systemctl.xml
+++ b/man/systemctl.xml
@@ -225,8 +225,8 @@ Sun 2017-02-26 20:57:49 EST 2h 3min left Sun 2017-02-26 11:56:36 EST 6h ago
Check whether any of the specified units is in the "failed" state. If no unit is specified,
- check whether there are any failed units, which corresponds to the degraded state
- returned by is-system-running. Returns an exit code 0
+ check whether there are any failed units or ordering cycles, which corresponds to the degraded
+ state returned by is-system-running. Returns an exit code 0
if at least one has failed, non-zero otherwise. Unless is specified, this
will also print the current unit or system state to standard output.
diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c
index 2610442384..ac203f2971 100644
--- a/src/core/dbus-manager.c
+++ b/src/core/dbus-manager.c
@@ -455,6 +455,35 @@ static int property_get_oom_score_adjust(
return sd_bus_message_append(reply, "i", n);
}
+static int property_get_transactions_with_cycle(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ Manager *m = ASSERT_PTR(userdata);
+ int r;
+
+ assert(bus);
+ assert(reply);
+
+ r = sd_bus_message_open_container(reply, 'a', "t");
+ if (r < 0)
+ return r;
+
+ uint64_t *id;
+ SET_FOREACH(id, m->transactions_with_cycle) {
+ r = sd_bus_message_append_basic(reply, 't', id);
+ if (r < 0)
+ return r;
+ }
+
+ return sd_bus_message_close_container(reply);
+}
+
static int bus_get_unit_by_name(Manager *m, sd_bus_message *message, const char *name, Unit **ret_unit, sd_bus_error *error) {
Unit *u;
int r;
@@ -2870,6 +2899,7 @@ const sd_bus_vtable bus_manager_vtable[] = {
SD_BUS_PROPERTY("NJobs", "u", property_get_hashmap_size, offsetof(Manager, jobs), 0),
SD_BUS_PROPERTY("NInstalledJobs", "u", bus_property_get_unsigned, offsetof(Manager, n_installed_jobs), 0),
SD_BUS_PROPERTY("NFailedJobs", "u", bus_property_get_unsigned, offsetof(Manager, n_failed_jobs), 0),
+ SD_BUS_PROPERTY("TransactionsWithOrderingCycle", "at", property_get_transactions_with_cycle, 0, 0),
SD_BUS_PROPERTY("Progress", "d", property_get_progress, 0, 0),
SD_BUS_PROPERTY("Environment", "as", property_get_environment, 0, 0),
SD_BUS_PROPERTY("ConfirmSpawn", "b", bus_property_get_bool, offsetof(Manager, confirm_spawn), SD_BUS_VTABLE_PROPERTY_CONST),
diff --git a/src/core/manager.c b/src/core/manager.c
index 0a6ea1658f..acf837941e 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -3675,6 +3675,8 @@ void manager_reset_failed(Manager *m) {
HASHMAP_FOREACH(u, m->units)
unit_reset_failed(u);
+
+ m->transactions_with_cycle = set_free(m->transactions_with_cycle);
}
bool manager_unit_inactive_or_pending(Manager *m, const char *name) {
@@ -4623,8 +4625,8 @@ ManagerState manager_state(Manager *m) {
return MANAGER_MAINTENANCE;
}
- /* Are there any failed units? If so, we are in degraded mode */
- if (!set_isempty(m->failed_units))
+ /* Are there any failed units or ordering cycles? If so, we are in degraded mode */
+ if (!set_isempty(m->failed_units) || !set_isempty(m->transactions_with_cycle))
return MANAGER_DEGRADED;
return MANAGER_RUNNING;
diff --git a/src/core/varlink-manager.c b/src/core/varlink-manager.c
index 3414ccb9ed..951e0b4b96 100644
--- a/src/core/varlink-manager.c
+++ b/src/core/varlink-manager.c
@@ -112,6 +112,24 @@ static int manager_context_build_json(sd_json_variant **ret, const char *name, v
JSON_BUILD_PAIR_STRING_NON_EMPTY("ControlGroup", m->cgroup_root));
}
+static int transactions_with_cycle_build_json(sd_json_variant **ret, const char *name, void *userdata) {
+ _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
+ const Set *ids = userdata;
+ int r;
+
+ assert(ret);
+
+ uint64_t *id;
+ SET_FOREACH(id, ids) {
+ r = sd_json_variant_append_arrayb(&v, SD_JSON_BUILD_UNSIGNED(*id));
+ if (r < 0)
+ return r;
+ }
+
+ *ret = TAKE_PTR(v);
+ return 0;
+}
+
static int manager_runtime_build_json(sd_json_variant **ret, const char *name, void *userdata) {
Manager *m = ASSERT_PTR(userdata);
dual_timestamp watchdog_last_ping;
@@ -154,6 +172,7 @@ static int manager_runtime_build_json(sd_json_variant **ret, const char *name, v
SD_JSON_BUILD_PAIR_UNSIGNED("NJobs", hashmap_size(m->jobs)),
SD_JSON_BUILD_PAIR_UNSIGNED("NInstalledJobs", m->n_installed_jobs),
SD_JSON_BUILD_PAIR_UNSIGNED("NFailedJobs", m->n_failed_jobs),
+ JSON_BUILD_PAIR_CALLBACK_NON_NULL("TransactionsWithOrderingCycle", transactions_with_cycle_build_json, m->transactions_with_cycle),
SD_JSON_BUILD_PAIR_REAL("Progress", manager_get_progress(m)),
JSON_BUILD_PAIR_DUAL_TIMESTAMP_NON_NULL("WatchdogLastPingTimestamp", watchdog_get_last_ping_as_dual_timestamp(&watchdog_last_ping)),
SD_JSON_BUILD_PAIR_STRING("SystemState", manager_state_to_string(manager_state(m))),
diff --git a/src/shared/varlink-io.systemd.Manager.c b/src/shared/varlink-io.systemd.Manager.c
index cdcaa53b71..a31e0e688b 100644
--- a/src/shared/varlink-io.systemd.Manager.c
+++ b/src/shared/varlink-io.systemd.Manager.c
@@ -155,6 +155,8 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE(
SD_VARLINK_DEFINE_FIELD(NInstalledJobs, SD_VARLINK_INT, 0),
SD_VARLINK_FIELD_COMMENT("The total amount of failed jobs"),
SD_VARLINK_DEFINE_FIELD(NFailedJobs, SD_VARLINK_INT, 0),
+ SD_VARLINK_FIELD_COMMENT("IDs of transactions that encountered ordering cycle"),
+ SD_VARLINK_DEFINE_FIELD(TransactionsWithOrderingCycle, SD_VARLINK_INT, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Boot progress as a floating point value between 0.0 and 1.0"),
SD_VARLINK_DEFINE_FIELD(Progress, SD_VARLINK_FLOAT, 0),
SD_VARLINK_FIELD_COMMENT("Timestamp when the hardware watchdog was last pinged"),