chiark / gitweb /
systemd,systemctl: export condition status and show failing condition
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Tue, 25 Jun 2013 20:09:07 +0000 (16:09 -0400)
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Thu, 18 Jul 2013 03:41:10 +0000 (23:41 -0400)
$ systemctl --user status hoohoo
hoohoo.service
   Loaded: loaded (/home/zbyszek/.config/systemd/user/hoohoo.service; static)
   Active: inactive (dead)
           start condition failed at Tue 2013-06-25 18:08:42 EDT; 1s ago
           ConditionPathExists=/tmp/hoo was not met

Full information is exported over D-Bus:
  [(condition, trigger, negate, param, state),...]
where state is one of "failed" (<0), "untested" (0), "OK" (>0).
I've decided to use 0 for "untested", because it might be useful to
differentiate different types of failure later on, without breaking
compatibility.

systemctl shows the failing condition, if there was a non-trigger
failing condition, or says "none of the trigger conditions were met",
because there're often many trigger conditions, and they must all
fail for the condition to fail, so printing them all would consume
a lot of space, and bring unnecessary attention to something that is
quite low-level.

TODO
src/core/condition.c
src/core/condition.h
src/core/dbus-unit.c
src/core/dbus-unit.h
src/core/unit.c
src/systemctl/systemctl.c

diff --git a/TODO b/TODO
index 4b75539f0abe6c67a49e8b45ae177dd3341b0f71..753d1ccdb1daa285b693893c88e45962e71fb17c 100644 (file)
--- a/TODO
+++ b/TODO
@@ -532,8 +532,6 @@ Features:
   when done. That means clients don't get a successful method reply,
   but much rather a disconnect on success.
 
-* remember which condition failed for services, not just the fact that something failed
-
 * use opterr = 0 for all getopt tools
 
 * properly handle loop back mounts via fstab, especially regards to fsck/passno
index 2fbc5ad0e6914534ca2173c3fd06b7f1ab751266..6c387450afdd5d2aea3d7798a55e17e0de3e5db7 100644 (file)
@@ -250,7 +250,7 @@ static bool test_ac_power(const char *parameter) {
         return (on_ac_power() != 0) == !!r;
 }
 
-bool condition_test(Condition *c) {
+static bool condition_test(Condition *c) {
         assert(c);
 
         switch(c->type) {
@@ -358,6 +358,7 @@ bool condition_test_list(const char *unit, Condition *first) {
                                        c->parameter,
                                        b ? "succeeded" : "failed",
                                        unit);
+                c->state = b ? 1 : -1;
 
                 if (!c->trigger && !b)
                         return false;
@@ -377,12 +378,13 @@ void condition_dump(Condition *c, FILE *f, const char *prefix) {
                 prefix = "";
 
         fprintf(f,
-                "%s\t%s: %s%s%s\n",
+                "%s\t%s: %s%s%s %s\n",
                 prefix,
                 condition_type_to_string(c->type),
                 c->trigger ? "|" : "",
                 c->negate ? "!" : "",
-                c->parameter);
+                c->parameter,
+                c->state < 0 ? "failed" : c->state > 0 ? "succeeded" : "untested");
 }
 
 void condition_dump_list(Condition *first, FILE *f, const char *prefix) {
index 2ad77876e48d9802a9051896358aaa63e3b88cbd..1813b735a5b6c241335f26ac01607a959d69a440 100644 (file)
@@ -48,11 +48,14 @@ typedef enum ConditionType {
 
 typedef struct Condition {
         ConditionType type;
-        char *parameter;
 
         bool trigger:1;
         bool negate:1;
 
+        char *parameter;
+
+        int state;
+
         LIST_FIELDS(struct Condition, conditions);
 } Condition;
 
@@ -60,7 +63,6 @@ Condition* condition_new(ConditionType type, const char *parameter, bool trigger
 void condition_free(Condition *c);
 void condition_free_list(Condition *c);
 
-bool condition_test(Condition *c);
 bool condition_test_list(const char *unit, Condition *c);
 
 void condition_dump(Condition *c, FILE *f, const char *prefix);
index ba4d42652ee1c6fab268e0d2e35c89ebe0e0dd88..4cd3a13fd2c9f68e3a31792f16b7dda90932661a 100644 (file)
@@ -311,6 +311,58 @@ static int bus_unit_append_need_daemon_reload(DBusMessageIter *i, const char *pr
         return 0;
 }
 
+static int bus_property_append_condition(DBusMessageIter *i, const char *property, void *data) {
+        Condition **cp = data;
+        Condition *c;
+        const char *name, *param;
+        dbus_bool_t trigger, negate;
+        dbus_int32_t state;
+        DBusMessageIter sub;
+
+        assert(i);
+        assert(property);
+        assert(cp);
+
+        c = *cp;
+        assert(c);
+
+        name = condition_type_to_string(c->type);
+        param = c->parameter;
+        trigger = c->trigger;
+        negate = c->negate;
+        state = c->state;
+
+        if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub) ||
+            !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &name) ||
+            !dbus_message_iter_append_basic(&sub, DBUS_TYPE_BOOLEAN, &trigger) ||
+            !dbus_message_iter_append_basic(&sub, DBUS_TYPE_BOOLEAN, &negate) ||
+            !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &param) ||
+            !dbus_message_iter_append_basic(&sub, DBUS_TYPE_INT32, &state) ||
+            !dbus_message_iter_close_container(i, &sub))
+                return -ENOMEM;
+
+        return 0;
+}
+
+static int bus_property_append_condition_list(DBusMessageIter *i, const char *property, void *data) {
+        Condition **first = data, *c;
+        DBusMessageIter sub;
+
+        assert(i);
+        assert(data);
+
+        if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(sbbsi)", &sub))
+                return -ENOMEM;
+
+        LIST_FOREACH(conditions, c, *first)
+                bus_property_append_condition(&sub, property, &c);
+
+        if (!dbus_message_iter_close_container(i, &sub))
+                return -ENOMEM;
+
+        return 0;
+}
+
 static int bus_unit_append_load_error(DBusMessageIter *i, const char *property, void *data) {
         Unit *u = data;
         const char *name, *message;
@@ -975,68 +1027,69 @@ int bus_unit_set_properties(
 }
 
 const BusProperty bus_unit_properties[] = {
-        { "Id",                   bus_property_append_string,         "s", offsetof(Unit, id),                                         true },
-        { "Names",                bus_unit_append_names,             "as", 0 },
-        { "Following",            bus_unit_append_following,          "s", 0 },
-        { "Requires",             bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUIRES]),                true },
-        { "RequiresOverridable",  bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUIRES_OVERRIDABLE]),    true },
-        { "Requisite",            bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUISITE]),               true },
-        { "RequisiteOverridable", bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUISITE_OVERRIDABLE]),   true },
-        { "Wants",                bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_WANTS]),                   true },
-        { "BindsTo",              bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_BINDS_TO]),                true },
-        { "PartOf",               bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_PART_OF]),                 true },
-        { "RequiredBy",           bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY]),             true },
-        { "RequiredByOverridable",bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY_OVERRIDABLE]), true },
-        { "WantedBy",             bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_WANTED_BY]),               true },
-        { "BoundBy",              bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_BOUND_BY]),                true },
-        { "ConsistsOf",           bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_CONSISTS_OF]),             true },
-        { "Conflicts",            bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_CONFLICTS]),               true },
-        { "ConflictedBy",         bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_CONFLICTED_BY]),           true },
-        { "Before",               bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_BEFORE]),                  true },
-        { "After",                bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_AFTER]),                   true },
-        { "OnFailure",            bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_ON_FAILURE]),              true },
-        { "Triggers",             bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_TRIGGERS]),                true },
-        { "TriggeredBy",          bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_TRIGGERED_BY]),            true },
-        { "PropagatesReloadTo",   bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_PROPAGATES_RELOAD_TO]),    true },
-        { "ReloadPropagatedFrom", bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_RELOAD_PROPAGATED_FROM]),  true },
-        { "RequiresMountsFor",    bus_property_append_strv,          "as", offsetof(Unit, requires_mounts_for),                        true },
-        { "Documentation",        bus_property_append_strv,          "as", offsetof(Unit, documentation),                              true },
-        { "Description",          bus_unit_append_description,        "s", 0 },
-        { "LoadState",            bus_unit_append_load_state,         "s", offsetof(Unit, load_state)                         },
-        { "ActiveState",          bus_unit_append_active_state,       "s", 0 },
-        { "SubState",             bus_unit_append_sub_state,          "s", 0 },
-        { "FragmentPath",         bus_property_append_string,         "s", offsetof(Unit, fragment_path),                              true },
-        { "SourcePath",           bus_property_append_string,         "s", offsetof(Unit, source_path),                                true },
-        { "DropInPaths",          bus_property_append_strv,          "as", offsetof(Unit, dropin_paths),                               true },
-        { "UnitFileState",        bus_unit_append_file_state,         "s", 0 },
-        { "InactiveExitTimestamp",bus_property_append_usec,           "t", offsetof(Unit, inactive_exit_timestamp.realtime)   },
-        { "InactiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.monotonic)  },
-        { "ActiveEnterTimestamp", bus_property_append_usec,           "t", offsetof(Unit, active_enter_timestamp.realtime)    },
-        { "ActiveEnterTimestampMonotonic", bus_property_append_usec,  "t", offsetof(Unit, active_enter_timestamp.monotonic)   },
-        { "ActiveExitTimestamp",  bus_property_append_usec,           "t", offsetof(Unit, active_exit_timestamp.realtime)     },
-        { "ActiveExitTimestampMonotonic",  bus_property_append_usec,  "t", offsetof(Unit, active_exit_timestamp.monotonic)    },
-        { "InactiveEnterTimestamp", bus_property_append_usec,         "t", offsetof(Unit, inactive_enter_timestamp.realtime)  },
-        { "InactiveEnterTimestampMonotonic",bus_property_append_usec, "t", offsetof(Unit, inactive_enter_timestamp.monotonic) },
-        { "CanStart",             bus_unit_append_can_start,          "b", 0 },
-        { "CanStop",              bus_unit_append_can_stop,           "b", 0 },
-        { "CanReload",            bus_unit_append_can_reload,         "b", 0 },
-        { "CanIsolate",           bus_unit_append_can_isolate,        "b", 0 },
-        { "Job",                  bus_unit_append_job,             "(uo)", 0 },
-        { "StopWhenUnneeded",     bus_property_append_bool,           "b", offsetof(Unit, stop_when_unneeded)                 },
-        { "RefuseManualStart",    bus_property_append_bool,           "b", offsetof(Unit, refuse_manual_start)                },
-        { "RefuseManualStop",     bus_property_append_bool,           "b", offsetof(Unit, refuse_manual_stop)                 },
-        { "AllowIsolate",         bus_property_append_bool,           "b", offsetof(Unit, allow_isolate)                      },
-        { "DefaultDependencies",  bus_property_append_bool,           "b", offsetof(Unit, default_dependencies)               },
-        { "OnFailureIsolate",     bus_property_append_bool,           "b", offsetof(Unit, on_failure_isolate)                 },
-        { "IgnoreOnIsolate",      bus_property_append_bool,           "b", offsetof(Unit, ignore_on_isolate)                  },
-        { "IgnoreOnSnapshot",     bus_property_append_bool,           "b", offsetof(Unit, ignore_on_snapshot)                 },
-        { "NeedDaemonReload",     bus_unit_append_need_daemon_reload, "b", 0 },
-        { "JobTimeoutUSec",       bus_property_append_usec,           "t", offsetof(Unit, job_timeout)                        },
-        { "ConditionTimestamp",   bus_property_append_usec,           "t", offsetof(Unit, condition_timestamp.realtime)       },
-        { "ConditionTimestampMonotonic", bus_property_append_usec,    "t", offsetof(Unit, condition_timestamp.monotonic)      },
-        { "ConditionResult",      bus_property_append_bool,           "b", offsetof(Unit, condition_result)                   },
-        { "LoadError",            bus_unit_append_load_error,      "(ss)", 0 },
-        { "Transient",            bus_property_append_bool,           "b", offsetof(Unit, transient)                          },
+        { "Id",                              bus_property_append_string,                "s", offsetof(Unit, id),                                         true },
+        { "Names",                           bus_unit_append_names,                    "as", 0                                                                },
+        { "Following",                       bus_unit_append_following,                 "s", 0                                                                },
+        { "Requires",                        bus_unit_append_dependencies,             "as", offsetof(Unit, dependencies[UNIT_REQUIRES]),                true },
+        { "RequiresOverridable",             bus_unit_append_dependencies,             "as", offsetof(Unit, dependencies[UNIT_REQUIRES_OVERRIDABLE]),    true },
+        { "Requisite",                       bus_unit_append_dependencies,             "as", offsetof(Unit, dependencies[UNIT_REQUISITE]),               true },
+        { "RequisiteOverridable",            bus_unit_append_dependencies,             "as", offsetof(Unit, dependencies[UNIT_REQUISITE_OVERRIDABLE]),   true },
+        { "Wants",                           bus_unit_append_dependencies,             "as", offsetof(Unit, dependencies[UNIT_WANTS]),                   true },
+        { "BindsTo",                         bus_unit_append_dependencies,             "as", offsetof(Unit, dependencies[UNIT_BINDS_TO]),                true },
+        { "PartOf",                          bus_unit_append_dependencies,             "as", offsetof(Unit, dependencies[UNIT_PART_OF]),                 true },
+        { "RequiredBy",                      bus_unit_append_dependencies,             "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY]),             true },
+        { "RequiredByOverridable",           bus_unit_append_dependencies,             "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY_OVERRIDABLE]), true },
+        { "WantedBy",                        bus_unit_append_dependencies,             "as", offsetof(Unit, dependencies[UNIT_WANTED_BY]),               true },
+        { "BoundBy",                         bus_unit_append_dependencies,             "as", offsetof(Unit, dependencies[UNIT_BOUND_BY]),                true },
+        { "ConsistsOf",                      bus_unit_append_dependencies,             "as", offsetof(Unit, dependencies[UNIT_CONSISTS_OF]),             true },
+        { "Conflicts",                       bus_unit_append_dependencies,             "as", offsetof(Unit, dependencies[UNIT_CONFLICTS]),               true },
+        { "ConflictedBy",                    bus_unit_append_dependencies,             "as", offsetof(Unit, dependencies[UNIT_CONFLICTED_BY]),           true },
+        { "Before",                          bus_unit_append_dependencies,             "as", offsetof(Unit, dependencies[UNIT_BEFORE]),                  true },
+        { "After",                           bus_unit_append_dependencies,             "as", offsetof(Unit, dependencies[UNIT_AFTER]),                   true },
+        { "OnFailure",                       bus_unit_append_dependencies,             "as", offsetof(Unit, dependencies[UNIT_ON_FAILURE]),              true },
+        { "Triggers",                        bus_unit_append_dependencies,             "as", offsetof(Unit, dependencies[UNIT_TRIGGERS]),                true },
+        { "TriggeredBy",                     bus_unit_append_dependencies,             "as", offsetof(Unit, dependencies[UNIT_TRIGGERED_BY]),            true },
+        { "PropagatesReloadTo",              bus_unit_append_dependencies,             "as", offsetof(Unit, dependencies[UNIT_PROPAGATES_RELOAD_TO]),    true },
+        { "ReloadPropagatedFrom",            bus_unit_append_dependencies,             "as", offsetof(Unit, dependencies[UNIT_RELOAD_PROPAGATED_FROM]),  true },
+        { "RequiresMountsFor",               bus_property_append_strv,                 "as", offsetof(Unit, requires_mounts_for),                        true },
+        { "Documentation",                   bus_property_append_strv,                 "as", offsetof(Unit, documentation),                              true },
+        { "Description",                     bus_unit_append_description,               "s", 0                                                                },
+        { "LoadState",                       bus_unit_append_load_state,                "s", offsetof(Unit, load_state)                                       },
+        { "ActiveState",                     bus_unit_append_active_state,              "s", 0                                                                },
+        { "SubState",                        bus_unit_append_sub_state,                 "s", 0                                                                },
+        { "FragmentPath",                    bus_property_append_string,                "s", offsetof(Unit, fragment_path),                              true },
+        { "SourcePath",                      bus_property_append_string,                "s", offsetof(Unit, source_path),                                true },
+        { "DropInPaths",                     bus_property_append_strv,                 "as", offsetof(Unit, dropin_paths),                               true },
+        { "UnitFileState",                   bus_unit_append_file_state,                "s", 0                                                                },
+        { "InactiveExitTimestamp",           bus_property_append_usec,                  "t", offsetof(Unit, inactive_exit_timestamp.realtime)                 },
+        { "InactiveExitTimestampMonotonic",  bus_property_append_usec,                  "t", offsetof(Unit, inactive_exit_timestamp.monotonic)                },
+        { "ActiveEnterTimestamp",            bus_property_append_usec,                  "t", offsetof(Unit, active_enter_timestamp.realtime)                  },
+        { "ActiveEnterTimestampMonotonic",   bus_property_append_usec,                  "t", offsetof(Unit, active_enter_timestamp.monotonic)                 },
+        { "ActiveExitTimestamp",             bus_property_append_usec,                  "t", offsetof(Unit, active_exit_timestamp.realtime)                   },
+        { "ActiveExitTimestampMonotonic",    bus_property_append_usec,                  "t", offsetof(Unit, active_exit_timestamp.monotonic)                  },
+        { "InactiveEnterTimestamp",          bus_property_append_usec,                  "t", offsetof(Unit, inactive_enter_timestamp.realtime)                },
+        { "InactiveEnterTimestampMonotonic", bus_property_append_usec,                  "t", offsetof(Unit, inactive_enter_timestamp.monotonic)               },
+        { "CanStart",                        bus_unit_append_can_start,                 "b", 0                                                                },
+        { "CanStop",                         bus_unit_append_can_stop,                  "b", 0                                                                },
+        { "CanReload",                       bus_unit_append_can_reload,                "b", 0                                                                },
+        { "CanIsolate",                      bus_unit_append_can_isolate,               "b", 0                                                                },
+        { "Job",                             bus_unit_append_job,                    "(uo)", 0                                                                },
+        { "StopWhenUnneeded",                bus_property_append_bool,                  "b", offsetof(Unit, stop_when_unneeded)                               },
+        { "RefuseManualStart",               bus_property_append_bool,                  "b", offsetof(Unit, refuse_manual_start)                              },
+        { "RefuseManualStop",                bus_property_append_bool,                  "b", offsetof(Unit, refuse_manual_stop)                               },
+        { "AllowIsolate",                    bus_property_append_bool,                  "b", offsetof(Unit, allow_isolate)                                    },
+        { "DefaultDependencies",             bus_property_append_bool,                  "b", offsetof(Unit, default_dependencies)                             },
+        { "OnFailureIsolate",                bus_property_append_bool,                  "b", offsetof(Unit, on_failure_isolate)                               },
+        { "IgnoreOnIsolate",                 bus_property_append_bool,                  "b", offsetof(Unit, ignore_on_isolate)                                },
+        { "IgnoreOnSnapshot",                bus_property_append_bool,                  "b", offsetof(Unit, ignore_on_snapshot)                               },
+        { "NeedDaemonReload",                bus_unit_append_need_daemon_reload,        "b", 0                                                                },
+        { "JobTimeoutUSec",                  bus_property_append_usec,                  "t", offsetof(Unit, job_timeout)                                      },
+        { "ConditionTimestamp",              bus_property_append_usec,                  "t", offsetof(Unit, condition_timestamp.realtime)                     },
+        { "ConditionTimestampMonotonic",     bus_property_append_usec,                  "t", offsetof(Unit, condition_timestamp.monotonic)                    },
+        { "ConditionResult",                 bus_property_append_bool,                  "b", offsetof(Unit, condition_result)                                 },
+        { "Conditions",                      bus_property_append_condition_list, "a(sbbsi)", offsetof(Unit, conditions)                                       },
+        { "LoadError",                       bus_unit_append_load_error,             "(ss)", 0                                                                },
+        { "Transient",                       bus_property_append_bool,                  "b", offsetof(Unit, transient)                                        },
         {}
 };
 
index d3f7ec621e1d97ffe3220573b14861b1cf80739b..3064cd552a37385f1ce16e4bbd70070ab5685b42 100644 (file)
         "  <property name=\"ConditionTimestamp\" type=\"t\" access=\"read\"/>\n" \
         "  <property name=\"ConditionTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
         "  <property name=\"ConditionResult\" type=\"b\" access=\"read\"/>\n" \
+        "  <property name=\"Conditions\" type=\"a(sbbsi)\" access=\"read\"/>\n" \
         "  <property name=\"LoadError\" type=\"(ss)\" access=\"read\"/>\n" \
         "  <property name=\"Transient\" type=\"b\" access=\"read\"/>\n" \
         " </interface>\n"
index a201fa40417a2968bae3c5612f1f5fef6a9097fd..0e9329f8c9b1e993ef67b3f6d103ac00a7dc35a7 100644 (file)
@@ -1125,7 +1125,8 @@ int unit_start(Unit *u) {
         }
 
         /* Forward to the main object, if we aren't it. */
-        if ((following = unit_following(u))) {
+        following = unit_following(u);
+        if (following) {
                 log_debug_unit(u->id, "Redirecting start request from %s to %s.",
                                u->id, following->id);
                 return unit_start(following);
index 6fdbc417b6e28776344d68edb30451531c32e889..b3b679e0af0b5c5637c99cc81a46ee377a99aeec 100644 (file)
@@ -2534,6 +2534,10 @@ typedef struct UnitStatusInfo {
 
         usec_t condition_timestamp;
         bool condition_result;
+        bool failed_condition_trigger;
+        bool failed_condition_negate;
+        const char *failed_condition;
+        const char *failed_condition_param;
 
         /* Socket */
         unsigned n_accepted;
@@ -2676,10 +2680,15 @@ static void print_status_info(UnitStatusInfo *i) {
                 s1 = format_timestamp_relative(since1, sizeof(since1), i->condition_timestamp);
                 s2 = format_timestamp(since2, sizeof(since2), i->condition_timestamp);
 
-                if (s1)
-                        printf("          start condition failed at %s; %s\n", s2, s1);
-                else if (s2)
-                        printf("          start condition failed at %s\n", s2);
+                printf("           start condition failed at %s%s%s\n",
+                       s2, s1 ? "; " : "", s1 ? s1 : "");
+                if (i->failed_condition_trigger)
+                        printf("           none of the trigger conditions were met\n");
+                else if (i->failed_condition)
+                        printf("           %s=%s%s was not met\n",
+                               i->failed_condition,
+                               i->failed_condition_negate ? "!" : "",
+                               i->failed_condition_param);
         }
 
         if (i->sysfs_path)
@@ -3038,15 +3047,18 @@ static int status_property(const char *name, DBusMessageIter *iter, UnitStatusIn
                                 ExecStatusInfo *info;
                                 int r;
 
-                                if (!(info = new0(ExecStatusInfo, 1)))
+                                info = new0(ExecStatusInfo, 1);
+                                if (!info)
                                         return -ENOMEM;
 
-                                if (!(info->name = strdup(name))) {
+                                info->name = strdup(name);
+                                if (!info->name) {
                                         free(info);
                                         return -ENOMEM;
                                 }
 
-                                if ((r = exec_status_info_deserialize(&sub, info)) < 0) {
+                                r = exec_status_info_deserialize(&sub, info);
+                                if (r < 0) {
                                         free(info);
                                         return r;
                                 }
@@ -3056,7 +3068,8 @@ static int status_property(const char *name, DBusMessageIter *iter, UnitStatusIn
                                 dbus_message_iter_next(&sub);
                         }
 
-                } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Listen")) {
+                } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT &&
+                           streq(name, "Listen")) {
                         DBusMessageIter sub, sub2;
 
                         dbus_message_iter_recurse(iter, &sub);
@@ -3082,7 +3095,8 @@ static int status_property(const char *name, DBusMessageIter *iter, UnitStatusIn
 
                         return 0;
 
-                } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRING && streq(name, "DropInPaths")) {
+                } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRING &&
+                           streq(name, "DropInPaths")) {
                         int r = bus_parse_strv_iter(iter, &i->dropin_paths);
                         if (r < 0)
                                 return r;
@@ -3105,6 +3119,36 @@ static int status_property(const char *name, DBusMessageIter *iter, UnitStatusIn
 
                                 dbus_message_iter_next(&sub);
                         }
+
+                } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT &&
+                           streq(name, "Conditions")) {
+                        DBusMessageIter sub, sub2;
+
+                        dbus_message_iter_recurse(iter, &sub);
+                        while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
+                                const char *cond, *param;
+                                dbus_bool_t trigger, negate;
+                                dbus_int32_t state;
+
+                                dbus_message_iter_recurse(&sub, &sub2);
+                                log_debug("here");
+
+                                if(bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &cond, true) >= 0 &&
+                                   bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_BOOLEAN, &trigger, true) >= 0 &&
+                                   bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_BOOLEAN, &negate, true) >= 0 &&
+                                   bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &param, true) >= 0 &&
+                                   bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_INT32, &state, false) >= 0) {
+                                        log_debug("%s %d %d %s %d", cond, trigger, negate, param, state);
+                                        if (state < 0 && (!trigger || !i->failed_condition)) {
+                                                i->failed_condition = cond;
+                                                i->failed_condition_trigger = trigger;
+                                                i->failed_condition_negate = negate;
+                                                i->failed_condition_param = param;
+                                        }
+                                }
+
+                                dbus_message_iter_next(&sub);
+                        }
                 }
 
                 break;