[systemd-devel] [PATCH 2/2] systemd, systemctl: export and show failing condition

Zbigniew Jędrzejewski-Szmek zbyszek at in.waw.pl
Wed Jun 26 05:11:18 PDT 2013


$ 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
---
 TODO                      |  2 --
 src/core/condition.c      |  9 +++++++--
 src/core/condition.h      |  2 +-
 src/core/dbus-unit.c      | 34 +++++++++++++++++++++++++++++++
 src/core/dbus-unit.h      |  1 +
 src/core/load-fragment.c  | 17 ++++++++++------
 src/core/unit.c           | 12 +++++++----
 src/core/unit.h           |  4 ++++
 src/systemctl/systemctl.c | 51 ++++++++++++++++++++++++++++++++++++++++++-----
 9 files changed, 112 insertions(+), 20 deletions(-)

diff --git a/TODO b/TODO
index caba4e3..1aced8b 100644
--- a/TODO
+++ b/TODO
@@ -474,8 +474,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
diff --git a/src/core/condition.c b/src/core/condition.c
index 3807398..cf1ea2e 100644
--- a/src/core/condition.c
+++ b/src/core/condition.c
@@ -334,10 +334,13 @@ bool condition_test(Condition *c) {
         }
 }
 
-bool condition_test_list(const char *unit, Condition *first) {
+bool condition_test_list(const char *unit, Condition *first, Condition **failing) {
         Condition *c;
         int triggered = -1;
 
+        assert(failing);
+        *failing = NULL;
+
         /* If the condition list is empty, then it is true */
         if (!first)
                 return true;
@@ -359,8 +362,10 @@ bool condition_test_list(const char *unit, Condition *first) {
                                       b ? "succeeded" : "failed",
                                       unit);
 
-                if (!c->trigger && !b)
+                if (!c->trigger && !b) {
+                        *failing = c;
                         return false;
+                }
 
                 if (c->trigger && triggered <= 0)
                         triggered = b;
diff --git a/src/core/condition.h b/src/core/condition.h
index 2ad7787..be8faf6 100644
--- a/src/core/condition.h
+++ b/src/core/condition.h
@@ -61,7 +61,7 @@ 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);
+bool condition_test_list(const char *unit, Condition *c, Condition **failing);
 
 void condition_dump(Condition *c, FILE *f, const char *prefix);
 void condition_dump_list(Condition *c, FILE *f, const char *prefix);
diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c
index 8a7ab34..c3c7198 100644
--- a/src/core/dbus-unit.c
+++ b/src/core/dbus-unit.c
@@ -395,6 +395,39 @@ 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;
+        DBusMessageIter sub;
+
+        assert(i);
+        assert(property);
+        assert(cp);
+
+        c = *cp;
+        if (c) {
+                name = condition_type_to_string(c->type);
+                param = c->parameter;
+                trigger = c->trigger;
+                negate = c->negate;
+        } else {
+                name = param = "";
+                trigger = negate = false;
+        }
+
+        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_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;
@@ -1329,6 +1362,7 @@ const BusProperty bus_unit_properties[] = {
         { "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)                   },
+        { "ConditionFailed",      bus_property_append_condition, "(sbbs)", offsetof(Unit, failed_condition)                   },
         { "LoadError",            bus_unit_append_load_error,      "(ss)", 0 },
         { NULL, }
 };
diff --git a/src/core/dbus-unit.h b/src/core/dbus-unit.h
index 83932c5..aa995da 100644
--- a/src/core/dbus-unit.h
+++ b/src/core/dbus-unit.h
@@ -122,6 +122,7 @@
         "  <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=\"ConditionFailed\" type=\"(sbbs)\" access=\"read\"/>\n" \
         "  <property name=\"LoadError\" type=\"(ss)\" access=\"read\"/>\n" \
         " </interface>\n"
 
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index 15fabe8..9553472 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -1634,6 +1634,14 @@ int config_parse_ip_tos(const char *unit,
         return 0;
 }
 
+static void reset_conditions(Unit *u) {
+        assert(u);
+
+        condition_free_list(u->conditions);
+        u->conditions = NULL;
+        u->failed_condition = NULL;
+}
+
 int config_parse_unit_condition_path(const char *unit,
                                      const char *filename,
                                      unsigned line,
@@ -1657,8 +1665,7 @@ int config_parse_unit_condition_path(const char *unit,
 
         if (isempty(rvalue)) {
                 /* Empty assignment resets the list */
-                condition_free_list(u->conditions);
-                u->conditions = NULL;
+                reset_conditions(u);
                 return 0;
         }
 
@@ -1711,8 +1718,7 @@ int config_parse_unit_condition_string(const char *unit,
 
         if (isempty(rvalue)) {
                 /* Empty assignment resets the list */
-                condition_free_list(u->conditions);
-                u->conditions = NULL;
+                reset_conditions(u);
                 return 0;
         }
 
@@ -1758,8 +1764,7 @@ int config_parse_unit_condition_null(const char *unit,
 
         if (isempty(rvalue)) {
                 /* Empty assignment resets the list */
-                condition_free_list(u->conditions);
-                u->conditions = NULL;
+                reset_conditions(u);
                 return 0;
         }
 
diff --git a/src/core/unit.c b/src/core/unit.c
index 8a00fea..60a1ef1 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -958,7 +958,8 @@ bool unit_condition_test(Unit *u) {
         assert(u);
 
         dual_timestamp_get(&u->condition_timestamp);
-        u->condition_result = condition_test_list(u->id, u->conditions);
+        u->condition_result = condition_test_list(u->id, u->conditions,
+                                                  &u->failed_condition);
 
         return u->condition_result;
 }
@@ -1087,7 +1088,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);
@@ -2560,7 +2562,8 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
                 } else if (streq(l, "condition-result")) {
                         int b;
 
-                        if ((b = parse_boolean(v)) < 0)
+                        b = parse_boolean(v);
+                        if (b < 0)
                                 log_debug("Failed to parse condition result value %s", v);
                         else
                                 u->condition_result = b;
@@ -2568,7 +2571,8 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
                         continue;
                 }
 
-                if ((r = UNIT_VTABLE(u)->deserialize_item(u, l, v, fds)) < 0)
+                r = UNIT_VTABLE(u)->deserialize_item(u, l, v, fds);
+                if (r < 0)
                         return r;
         }
 }
diff --git a/src/core/unit.h b/src/core/unit.h
index da52101..4539522 100644
--- a/src/core/unit.h
+++ b/src/core/unit.h
@@ -166,6 +166,10 @@ struct Unit {
         /* Conditions to check */
         LIST_HEAD(Condition, conditions);
 
+        /* Condition which prevented start */
+        Condition *failed_condition;
+
+        /* Condition check time */
         dual_timestamp condition_timestamp;
 
         dual_timestamp inactive_exit_timestamp;
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index 24543ee..8f3fa64 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -2668,6 +2668,10 @@ typedef struct UnitStatusInfo {
 
         usec_t condition_timestamp;
         bool condition_result;
+        bool failed_condition_trigger:1;
+        bool failed_condition_negate:1;
+        const char *failed_condition;
+        const char *failed_condition_param;
 
         /* Socket */
         unsigned n_accepted;
@@ -2810,10 +2814,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) {
+                        printf("          %s=%s%s%s was not met\n",
+                               i->failed_condition,
+                               i->failed_condition_trigger ? "|" : "",
+                               i->failed_condition_negate ? "!" : "",
+                               i->failed_condition_param);
+                }
         }
 
         if (i->sysfs_path)
@@ -3172,7 +3181,8 @@ static int status_property(const char *name, DBusMessageIter *iter, UnitStatusIn
                                         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;
                                 }
@@ -3255,6 +3265,37 @@ static int status_property(const char *name, DBusMessageIter *iter, UnitStatusIn
 
                         if (!isempty(message))
                                 i->load_error = message;
+
+                } else if (streq(name, "ConditionFailed")) {
+                        DBusMessageIter sub;
+                        const char *cond, *param;
+                        dbus_bool_t trigger, negate;
+                        int r;
+
+                        dbus_message_iter_recurse(iter, &sub);
+
+                        r = bus_iter_get_basic_and_next(&sub, DBUS_TYPE_STRING, &cond, true);
+                        if (r < 0)
+                                return r;
+
+                        r = bus_iter_get_basic_and_next(&sub, DBUS_TYPE_BOOLEAN, &trigger, true);
+                        if (r < 0)
+                                return r;
+
+                        r = bus_iter_get_basic_and_next(&sub, DBUS_TYPE_BOOLEAN, &negate, true);
+                        if (r < 0)
+                                return r;
+
+                        r = bus_iter_get_basic_and_next(&sub, DBUS_TYPE_STRING, &param, false);
+                        if (r < 0)
+                                return r;
+
+                        if (!isempty(cond)) {
+                                i->failed_condition = cond;
+                                i->failed_condition_param = param;
+                                i->failed_condition_trigger = trigger;
+                                i->failed_condition_negate = negate;
+                        }
                 }
 
                 break;
-- 
1.8.2.562.g931e949



More information about the systemd-devel mailing list