[systemd-commits] 5 commits - configure.ac man/journalctl.xml src/core src/journal src/python-systemd src/systemctl TODO

Zbigniew Jędrzejewski-Szmek zbyszek at kemper.freedesktop.org
Wed Jul 17 20:46:56 PDT 2013


 TODO                          |    2 
 configure.ac                  |    1 
 man/journalctl.xml            |   74 +++++++++--------
 src/core/condition.c          |   20 +++-
 src/core/condition.h          |    8 +
 src/core/dbus-unit.c          |  177 +++++++++++++++++++++++++++---------------
 src/core/dbus-unit.h          |    1 
 src/core/unit.c               |    5 -
 src/journal/journalctl.c      |  164 ++++++++++++++++++--------------------
 src/python-systemd/_daemon.c  |   22 -----
 src/python-systemd/_reader.c  |   84 ++++++++++++++++---
 src/python-systemd/journal.py |   31 +++----
 src/python-systemd/pyutil.c   |   20 ++++
 src/python-systemd/pyutil.h   |    4 
 src/systemctl/systemctl.c     |   62 ++++++++++++--
 15 files changed, 425 insertions(+), 250 deletions(-)

New commits:
commit 52990c2e0eabd1c11280f553f858062d4165b92f
Author: Zbigniew Jędrzejewski-Szmek <zbyszek at in.waw.pl>
Date:   Tue Jun 25 16:09:07 2013 -0400

    systemd,systemctl: export condition status and show failing condition
    
    $ 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.

diff --git a/TODO b/TODO
index 4b75539..753d1cc 100644
--- 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
diff --git a/src/core/condition.c b/src/core/condition.c
index 2fbc5ad..6c38745 100644
--- a/src/core/condition.c
+++ b/src/core/condition.c
@@ -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) {
diff --git a/src/core/condition.h b/src/core/condition.h
index 2ad7787..1813b73 100644
--- a/src/core/condition.h
+++ b/src/core/condition.h
@@ -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);
diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c
index ba4d426..4cd3a13 100644
--- a/src/core/dbus-unit.c
+++ b/src/core/dbus-unit.c
@@ -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)                                        },
         {}
 };
 
diff --git a/src/core/dbus-unit.h b/src/core/dbus-unit.h
index d3f7ec6..3064cd5 100644
--- a/src/core/dbus-unit.h
+++ b/src/core/dbus-unit.h
@@ -125,6 +125,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=\"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"
diff --git a/src/core/unit.c b/src/core/unit.c
index a201fa4..0e9329f 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -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);
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index 6fdbc41..b3b679e 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -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;

commit 4b744dfabebd10bf0f13b64060f44b1bd6c82704
Author: Zbigniew Jędrzejewski-Szmek <zbyszek at in.waw.pl>
Date:   Tue Jun 25 12:15:07 2013 -0400

    systemd: log failed conditions
    
    ConditionPathExists=/tmp/nosuchpath failed for nosuchpath.service.

diff --git a/src/core/condition.c b/src/core/condition.c
index 427aa08..2fbc5ad 100644
--- a/src/core/condition.c
+++ b/src/core/condition.c
@@ -37,6 +37,7 @@
 #include "virt.h"
 #include "path-util.h"
 #include "fileio.h"
+#include "unit.h"
 
 Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate) {
         Condition *c;
@@ -333,7 +334,7 @@ bool condition_test(Condition *c) {
         }
 }
 
-bool condition_test_list(Condition *first) {
+bool condition_test_list(const char *unit, Condition *first) {
         Condition *c;
         int triggered = -1;
 
@@ -348,6 +349,15 @@ bool condition_test_list(Condition *first) {
                 bool b;
 
                 b = condition_test(c);
+                if (unit)
+                        log_debug_unit(unit,
+                                       "%s=%s%s%s %s for %s.",
+                                       condition_type_to_string(c->type),
+                                       c->trigger ? "|" : "",
+                                       c->negate ? "!" : "",
+                                       c->parameter,
+                                       b ? "succeeded" : "failed",
+                                       unit);
 
                 if (!c->trigger && !b)
                         return false;
diff --git a/src/core/condition.h b/src/core/condition.h
index 50ed955..2ad7787 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(Condition *c);
+bool condition_test_list(const char *unit, Condition *c);
 
 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/unit.c b/src/core/unit.c
index f4f92f0..a201fa4 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -996,7 +996,7 @@ bool unit_condition_test(Unit *u) {
         assert(u);
 
         dual_timestamp_get(&u->condition_timestamp);
-        u->condition_result = condition_test_list(u->conditions);
+        u->condition_result = condition_test_list(u->id, u->conditions);
 
         return u->condition_result;
 }

commit d121b396ccb19dc33aacfc75a7f7a719914267c8
Author: Zbigniew Jędrzejewski-Szmek <zbyszek at in.waw.pl>
Date:   Tue Jul 16 15:56:22 2013 -0400

    journalctl: remove ":" from the --boot syntax
    
    Instead of :-0, :1, :5, etc., use -0, 1 or +1, 5, etc. For BOOT_ID+OFFSET,
    use BOOT_ID+offset or BOOT_ID-offset (either + or - is required).
    
    Also make error handling a bit more robust and verbose.
    Modify the man page to describe the most common case (-b) first,
    and the second most common case (-b -1) second.

diff --git a/man/journalctl.xml b/man/journalctl.xml
index 3e03c45..65a59ea 100644
--- a/man/journalctl.xml
+++ b/man/journalctl.xml
@@ -399,42 +399,50 @@
                         </varlistentry>
 
                         <varlistentry>
-                                <term><option>-b <optional><replaceable>ID</replaceable></optional></option></term>
-                                <term><option>--boot=<optional><replaceable>ID</replaceable></optional></option></term>
+                                <term><option>-b <optional><replaceable>ID</replaceable></optional><optional><replaceable>±offset</replaceable></optional></option></term>
+                                <term><option>--boot=<optional><replaceable>ID</replaceable></optional><optional><replaceable>±offset</replaceable></optional></option></term>
 
-                                <listitem><para>Show messages from the specified
-                                boot <replaceable>ID</replaceable> or from
-                                current boot if no <replaceable>ID</replaceable>
-                                is given. This will add a match for
+                                <listitem><para>Show messages from a specific
+                                boot. This will add a match for
                                 <literal>_BOOT_ID=</literal>.</para>
 
-                                <para>The argument is a 128 bit ID given in
-                                short or UUID form and optionally followed by
-                                <literal>:n</literal> which identifies the nth
-                                boot relative to the boot ID given to the left
-                                of <literal>:</literal>. Supplying a negative
-                                value for n will look for a past boot and a
-                                positive value for a future boot. The boot IDs
-                                are searched for in chronological order. If no
-                                number is provided after <literal>:</literal>,
-                                <literal>-1</literal> is assumed. A value of 0
-                                is valid and equivalent to omitting
-                                <literal>:0</literal>.</para>
-
-                                <para>Alternatively, the argument may constist
-                                only of <literal>:n</literal>. In this case, a
-                                positive value will look up the nth boot
-                                starting from the beginning of the jouranl, a
-                                negative value will look up a previous boot
-                                relative to the current boot. <literal>:0</literal>
-                                will look for the current boot ID. Thus,
-                                <literal>:1</literal> is the first boot found in
-                                the journal, <literal>:2</literal> the second
-                                and so on; while <literal>:-1</literal> is the
-                                previous boot, <literal>:-2</literal> the boot
-                                before that and so on. Omitting a value after
-                                <literal>:</literal> will look for the previous
-                                boot.</para></listitem>
+                                <para>The argument may be empty, in which case
+                                logs for the current boot will be shown.</para>
+
+                                <para>If the boot ID is omitted, a positive
+                                <replaceable>offset</replaceable> will look up
+                                the boots starting from the beginning of the
+                                journal, and a equal-or-less-than zero
+                                <replaceable>offset</replaceable> will look up
+                                boots starting from the end of the
+                                journal. Thus, <constant>1</constant> means the
+                                first boot found in the journal in the
+                                chronological order, <constant>2</constant> the
+                                second and so on; while <constant>-0</constant>
+                                is the last boot, <constant>-1</constant> the
+                                boot before that, and so on. An empty
+                                <replaceable>offset</replaceable> is equivalent
+                                to specifying <constant>-0</constant>, except
+                                when the current boot is not the last boot
+                                (e.g. because <option>--directory</option> was
+                                specified to look at logs from a different
+                                machine).</para>
+
+                                <para>If the 32 character
+                                <replaceable>ID</replaceable> is specified, it
+                                may optionally be followed by
+                                <replaceable>offset</replaceable> which
+                                identifies the boot relative to the one given by
+                                boot <replaceable>ID</replaceable>. Negative
+                                values mean earlier boots and a positive values
+                                mean later boots. If
+                                <replaceable>offset</replaceable> is not
+                                specified, a value of zero is assumed and the
+                                logs for the boot given by
+                                <replaceable>ID</replaceable> are shown.
+                                </para>
+
+                                </listitem>
                         </varlistentry>
 
                         <varlistentry>
diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c
index 7099706..9a40d69 100644
--- a/src/journal/journalctl.c
+++ b/src/journal/journalctl.c
@@ -70,8 +70,8 @@ static int arg_lines = -1;
 static bool arg_no_tail = false;
 static bool arg_quiet = false;
 static bool arg_merge = false;
-static bool arg_boot_id = false;
-static char *arg_boot_id_descriptor = NULL;
+static bool arg_boot = false;
+static char *arg_boot_descriptor = NULL;
 static bool arg_dmesg = false;
 static const char *arg_cursor = NULL;
 static const char *arg_after_cursor = NULL;
@@ -346,17 +346,24 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
 
                 case 'b':
+                        arg_boot = true;
+
                         if (optarg)
-                                arg_boot_id_descriptor = optarg;
-                        else if (optind < argc && argv[optind][0] != '-') {
-                                arg_boot_id_descriptor = argv[optind];
-                                optind++;
+                                arg_boot_descriptor = optarg;
+                        else if (optind < argc) {
+                                int boot;
+
+                                if (argv[optind][0] != '-' ||
+                                    safe_atoi(argv[optind], &boot) >= 0) {
+                                        arg_boot_descriptor = argv[optind];
+                                        optind++;
+                                }
                         }
-                        arg_boot_id = true;
+
                         break;
 
                 case 'k':
-                        arg_boot_id = arg_dmesg = true;
+                        arg_boot = arg_dmesg = true;
                         break;
 
                 case ARG_SYSTEM:
@@ -671,23 +678,17 @@ static int boot_id_cmp(const void *a, const void *b) {
 static int get_relative_boot_id(sd_journal *j, sd_id128_t *boot_id, int relative) {
         int r;
         const void *data;
-        unsigned int id_count = 0;
+        unsigned int count = 0;
         size_t length, allocated = 0;
-        boot_id_t ref_boot_id, *id;
+        boot_id_t ref_boot_id = {SD_ID128_NULL}, *id;
         _cleanup_free_ boot_id_t *all_ids = NULL;
-        bool find_first_boot = false, ref_boot_found = false;
 
         assert(j);
         assert(boot_id);
 
-        if (relative == 0)
+        if (relative == 0 && !sd_id128_equal(*boot_id, SD_ID128_NULL))
                 return 0;
 
-        if (sd_id128_equal(*boot_id, SD_ID128_NULL) && relative > 0) {
-                find_first_boot = true;
-                relative--;
-        }
-
         r = sd_journal_query_unique(j, "_BOOT_ID");
         if (r < 0)
                 return r;
@@ -696,123 +697,118 @@ static int get_relative_boot_id(sd_journal *j, sd_id128_t *boot_id, int relative
                 if (length < strlen("_BOOT_ID="))
                         continue;
 
-                if (!GREEDY_REALLOC(all_ids, allocated, id_count + 1))
+                if (!GREEDY_REALLOC(all_ids, allocated, count + 1))
                         return log_oom();
 
-                id = &all_ids[id_count];
+                id = &all_ids[count];
 
                 r = sd_id128_from_string(((const char *)data) + strlen("_BOOT_ID="), &id->id);
-                if (r < 0) {
+                if (r < 0)
                         continue;
-                }
 
-                sd_journal_flush_matches(j);
                 r = sd_journal_add_match(j, data, length);
                 if (r < 0)
-                        continue;
+                        return r;
 
                 r = sd_journal_seek_head(j);
                 if (r < 0)
-                        continue;
+                        return r;
 
                 r = sd_journal_next(j);
-                if (r <= 0)
-                        continue;
+                if (r < 0)
+                        return r;
+                else if (r == 0)
+                        goto flush;
 
                 r = sd_journal_get_realtime_usec(j, &id->timestamp);
                 if (r < 0)
-                        continue;
+                        return r;
 
-                if (!find_first_boot && sd_id128_equal(id->id, *boot_id)) {
+                if (sd_id128_equal(id->id, *boot_id))
                         ref_boot_id = *id;
-                        ref_boot_found = true;
-                }
 
-                id_count++;
+                count++;
+        flush:
+                sd_journal_flush_matches(j);
         }
 
-        *boot_id = SD_ID128_NULL;
-        sd_journal_flush_matches(j);
+        qsort(all_ids, count, sizeof(boot_id_t), boot_id_cmp);
 
-        if (id_count == 0 || (!find_first_boot && !ref_boot_found))
-                return 0;
+        if (sd_id128_equal(*boot_id, SD_ID128_NULL)) {
+                if (relative > (int) count || relative <= -(int)count)
+                        return -EADDRNOTAVAIL;
 
-        qsort(all_ids, id_count, sizeof(boot_id_t), boot_id_cmp);
-        if (find_first_boot)
-                id = all_ids;
-        else
-                id = bsearch(&ref_boot_id, all_ids, id_count, sizeof(boot_id_t), boot_id_cmp);
+                *boot_id = all_ids[(relative <= 0)*count + relative - 1].id;
+        } else {
+                id = bsearch(&ref_boot_id, all_ids, count, sizeof(boot_id_t), boot_id_cmp);
 
-        if (!id || (relative < 0 && ((id - all_ids) + relative) < 0) ||
-            (relative >= 0 && (unsigned long)((id - all_ids) + relative) >= id_count))
-                return 0;
+                if (!id ||
+                    relative <= 0 ? (id - all_ids) + relative < 0 :
+                                    (id - all_ids) + relative >= count)
+                        return -EADDRNOTAVAIL;
+
+                *boot_id = (id + relative)->id;
+        }
 
-        id += relative;
-        *boot_id = id->id;
         return 0;
 }
 
 static int add_boot(sd_journal *j) {
         char match[9+32+1] = "_BOOT_ID=";
-        char *marker;
-        sd_id128_t boot_id;
+        char *offset;
+        sd_id128_t boot_id = SD_ID128_NULL;
         int r, relative = 0;
 
         assert(j);
 
-        if (!arg_boot_id)
+        if (!arg_boot)
                 return 0;
 
-        if (arg_boot_id_descriptor) {
-                marker = strchr(arg_boot_id_descriptor, ':');
-                if (marker) {
-                        *marker = '\0';
-                        marker++;
+        if (!arg_boot_descriptor)
+                return add_match_this_boot(j);
 
-                        if (*marker == '\0')
-                                relative = -1;
-                        else {
-                                r = safe_atoi(marker, &relative);
-                                if (r < 0) {
-                                        log_error("Failed to parse relative boot ID number '%s'", marker);
-                                        return -EINVAL;
-                                }
-                        }
+        if (strlen(arg_boot_descriptor) >= 32) {
+                char tmp = arg_boot_descriptor[32];
+                arg_boot_descriptor[32] = '\0';
+                r = sd_id128_from_string(arg_boot_descriptor, &boot_id);
+                arg_boot_descriptor[32] = tmp;
+
+                if (r < 0) {
+                        log_error("Failed to parse boot ID '%.32s': %s",
+                                  arg_boot_descriptor, strerror(-r));
+                        return r;
                 }
-        }
 
-        if (isempty(arg_boot_id_descriptor)) {
-                if (relative > 0) {
-                        /* We cannot look into the future. Instead, we look
-                         * into the past (starting from first boot). The ID
-                         * will be looked up later */
-                        boot_id = SD_ID128_NULL;
-                } else {
-                        r = sd_id128_get_boot(&boot_id);
-                        if (r < 0) {
-                                log_error("Failed to get boot ID: %s", strerror(-r));
-                                return r;
-                        }
+                offset = arg_boot_descriptor + 32;
+
+                if (*offset != '-' && *offset != '+') {
+                        log_error("Relative boot ID offset must start with a '+' or a '-', found '%s' ", offset);
+                        return -EINVAL;
                 }
-        } else {
-                r = sd_id128_from_string(arg_boot_id_descriptor, &boot_id);
+        } else
+                offset = arg_boot_descriptor;
+
+        if (*offset) {
+                r = safe_atoi(offset, &relative);
                 if (r < 0) {
-                        log_error("Failed to parse boot ID: %s", strerror(-r));
-                        return r;
+                        log_error("Failed to parse relative boot ID number '%s'", offset);
+                        return -EINVAL;
                 }
         }
 
         r = get_relative_boot_id(j, &boot_id, relative);
         if (r < 0) {
-                log_error("Failed to look up boot ID: %s", strerror(-r));
+                if (sd_id128_equal(boot_id, SD_ID128_NULL))
+                        log_error("Failed to look up boot %+d: %s", relative, strerror(-r));
+                else
+                        log_error("Failed to look up boot ID "SD_ID128_FORMAT_STR"%+d: %s",
+                                  SD_ID128_FORMAT_VAL(boot_id), relative, strerror(-r));
                 return r;
-        } else if (sd_id128_equal(boot_id, SD_ID128_NULL)) {
-                log_error("Failed to find boot ID");
-                return -1;
         }
 
         sd_id128_to_string(boot_id, match + 9);
-        r = sd_journal_add_match(j, match, strlen(match));
+
+        r = sd_journal_add_match(j, match, sizeof(match) - 1);
         if (r < 0) {
                 log_error("Failed to add match: %s", strerror(-r));
                 return r;

commit affba8e90243526be673ad9f9b306a740b8824a6
Author: Zbigniew Jędrzejewski-Szmek <zbyszek at in.waw.pl>
Date:   Wed Jul 17 12:50:13 2013 -0400

    systemd-python: add support for sd_j_open_files
    
    Also export missing flags.

diff --git a/src/python-systemd/_daemon.c b/src/python-systemd/_daemon.c
index bd4e73e..6b84fb8 100644
--- a/src/python-systemd/_daemon.c
+++ b/src/python-systemd/_daemon.c
@@ -40,28 +40,6 @@ PyDoc_STRVAR(module__doc__,
         "running under systemd."
 );
 
-
-#if PY_MAJOR_VERSION >=3 && PY_MINOR_VERSION >= 1
-static int Unicode_FSConverter(PyObject* obj, void *_result) {
-        PyObject **result = _result;
-
-        assert(result);
-
-        if (!obj)
-                /* cleanup: we don't return Py_CLEANUP_SUPPORTED, so
-                 * we can assume that it was PyUnicode_FSConverter. */
-                return PyUnicode_FSConverter(obj, result);
-
-        if (obj == Py_None) {
-                *result = NULL;
-                return 1;
-        }
-
-        return PyUnicode_FSConverter(obj, result);
-}
-#endif
-
-
 PyDoc_STRVAR(booted__doc__,
              "booted() -> bool\n\n"
              "Return True iff this system is running under systemd.\n"
diff --git a/src/python-systemd/_reader.c b/src/python-systemd/_reader.c
index 2c69963..6ac2f20 100644
--- a/src/python-systemd/_reader.c
+++ b/src/python-systemd/_reader.c
@@ -30,6 +30,7 @@
 #include "pyutil.h"
 #include "macro.h"
 #include "util.h"
+#include "strv.h"
 #include "build.h"
 
 typedef struct {
@@ -63,6 +64,57 @@ static PyStructSequence_Desc Monotonic_desc = {
 };
 #endif
 
+static int strv_converter(PyObject* obj, void *_result) {
+        char ***result = _result;
+        Py_ssize_t i, len;
+
+        assert(result);
+
+        if (!obj)
+            goto cleanup;
+
+        if (!PySequence_Check(obj))
+            return 0;
+
+        len = PySequence_Length(obj);
+        *result = new0(char*, len + 1);
+
+        for (i = 0; i < len; i++) {
+            PyObject *item;
+#if PY_MAJOR_VERSION >=3 && PY_MINOR_VERSION >= 1
+            int r;
+            PyObject *bytes;
+#endif
+            char *s, *s2;
+
+            item = PySequence_ITEM(obj, i);
+#if PY_MAJOR_VERSION >=3 && PY_MINOR_VERSION >= 1
+            r = PyUnicode_FSConverter(item, &bytes);
+            if (r == 0)
+                goto cleanup;
+
+            s = PyBytes_AsString(bytes);
+#else
+            s = PyString_AsString(item);
+#endif
+            if (!s)
+                goto cleanup;
+
+            s2 = strdup(s);
+            if (!s2)
+                log_oom();
+
+            (*result)[i] = s2;
+        }
+
+        return 1;
+
+cleanup:
+        strv_free(*result);
+        *result = NULL;
+
+        return 0;
+}
 
 static void Reader_dealloc(Reader* self)
 {
@@ -71,40 +123,45 @@ static void Reader_dealloc(Reader* self)
 }
 
 PyDoc_STRVAR(Reader__doc__,
-             "_Reader([flags | path]) -> ...\n\n"
+             "_Reader([flags | path | files]) -> ...\n\n"
              "_Reader allows filtering and retrieval of Journal entries.\n"
              "Note: this is a low-level interface, and probably not what you\n"
              "want, use systemd.journal.Reader instead.\n\n"
              "Argument `flags` sets open flags of the journal, which can be one\n"
              "of, or ORed combination of constants: LOCAL_ONLY (default) opens\n"
              "journal on local machine only; RUNTIME_ONLY opens only\n"
-             "volatile journal files; and SYSTEM_ONLY opens only\n"
-             "journal files of system services and the kernel.\n\n"
-             "Argument `path` is the directory of journal files. Note that\n"
-             "`flags` and `path` are exclusive.\n\n"
+             "volatile journal files; and SYSTEM opens journal files of\n"
+             "system services and the kernel, and CURRENT_USER opens files\n"
+             "of the current user.\n\n"
+             "Argument `path` is the directory of journal files.\n"
+             "Argument `files` is a list of files. Note that\n"
+             "`flags`, `path`, and `files` are exclusive.\n\n"
              "_Reader implements the context manager protocol: the journal\n"
              "will be closed when exiting the block.");
 static int Reader_init(Reader *self, PyObject *args, PyObject *keywds)
 {
     int flags = 0, r;
     char *path = NULL;
+    char **files = NULL;
 
-    static const char* const kwlist[] = {"flags", "path", NULL};
-    if (!PyArg_ParseTupleAndKeywords(args, keywds, "|iz", (char**) kwlist,
-                                     &flags, &path))
+    static const char* const kwlist[] = {"flags", "path", "files", NULL};
+    if (!PyArg_ParseTupleAndKeywords(args, keywds, "|izO&", (char**) kwlist,
+                                     &flags, &path, strv_converter, &files))
         return -1;
 
+    if (!!flags + !!path + !!files > 1) {
+        PyErr_SetString(PyExc_ValueError, "cannot use more than one of flags, path, and files");
+        return -1;
+    }
+
     if (!flags)
         flags = SD_JOURNAL_LOCAL_ONLY;
-    else
-        if (path) {
-            PyErr_SetString(PyExc_ValueError, "cannot use both flags and path");
-            return -1;
-        }
 
     Py_BEGIN_ALLOW_THREADS
     if (path)
         r = sd_journal_open_directory(&self->j, path, 0);
+    else if (files)
+        r = sd_journal_open_files(&self->j, (const char**) files, 0);
     else
         r = sd_journal_open(&self->j, flags);
     Py_END_ALLOW_THREADS
@@ -1099,6 +1156,7 @@ init_reader(void)
         PyModule_AddIntConstant(m, "RUNTIME_ONLY", SD_JOURNAL_RUNTIME_ONLY) ||
         PyModule_AddIntConstant(m, "SYSTEM", SD_JOURNAL_SYSTEM) ||
         PyModule_AddIntConstant(m, "SYSTEM_ONLY", SD_JOURNAL_SYSTEM_ONLY) ||
+        PyModule_AddIntConstant(m, "CURRENT_USER", SD_JOURNAL_CURRENT_USER) ||
         PyModule_AddStringConstant(m, "__version__", PACKAGE_VERSION)) {
 #if PY_MAJOR_VERSION >= 3
         Py_DECREF(m);
diff --git a/src/python-systemd/journal.py b/src/python-systemd/journal.py
index adcc844..d0bcd24 100644
--- a/src/python-systemd/journal.py
+++ b/src/python-systemd/journal.py
@@ -33,7 +33,8 @@ from syslog import (LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR,
                     LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG)
 from ._journal import __version__, sendv, stream_fd
 from ._reader import (_Reader, NOP, APPEND, INVALIDATE,
-                      LOCAL_ONLY, RUNTIME_ONLY, SYSTEM_ONLY,
+                      LOCAL_ONLY, RUNTIME_ONLY,
+                      SYSTEM, SYSTEM_ONLY, CURRENT_USER,
                       _get_catalog)
 from . import id128 as _id128
 
@@ -123,7 +124,7 @@ class Reader(_Reader):
     See systemd.journal-fields(7) for more info on typical fields
     found in the journal.
     """
-    def __init__(self, flags=0, path=None, converters=None):
+    def __init__(self, flags=0, path=None, files=None, converters=None):
         """Create an instance of Reader, which allows filtering and
         return of journal entries.
 
@@ -149,7 +150,7 @@ class Reader(_Reader):
         Reader implements the context manager protocol: the journal
         will be closed when exiting the block.
         """
-        super(Reader, self).__init__(flags, path)
+        super(Reader, self).__init__(flags, path, files)
         if _sys.version_info >= (3,3):
             self.converters = _ChainMap()
             if converters is not None:
diff --git a/src/python-systemd/pyutil.c b/src/python-systemd/pyutil.c
index 42e7ba7..722c4f5 100644
--- a/src/python-systemd/pyutil.c
+++ b/src/python-systemd/pyutil.c
@@ -58,3 +58,23 @@ int set_error(int r, const char* path, const char* invalid_message) {
         }
         return -1;
 }
+
+#if PY_MAJOR_VERSION >=3 && PY_MINOR_VERSION >= 1
+int Unicode_FSConverter(PyObject* obj, void *_result) {
+        PyObject **result = _result;
+
+        assert(result);
+
+        if (!obj)
+                /* cleanup: we don't return Py_CLEANUP_SUPPORTED, so
+                 * we can assume that it was PyUnicode_FSConverter. */
+                return PyUnicode_FSConverter(obj, result);
+
+        if (obj == Py_None) {
+                *result = NULL;
+                return 1;
+        }
+
+        return PyUnicode_FSConverter(obj, result);
+}
+#endif
diff --git a/src/python-systemd/pyutil.h b/src/python-systemd/pyutil.h
index ea88840..1477e7b 100644
--- a/src/python-systemd/pyutil.h
+++ b/src/python-systemd/pyutil.h
@@ -30,6 +30,10 @@ void cleanup_Py_DECREFp(PyObject **p);
 PyObject* absolute_timeout(uint64_t t);
 int set_error(int r, const char* path, const char* invalid_message);
 
+#if PY_MAJOR_VERSION >=3 && PY_MINOR_VERSION >= 1
+int Unicode_FSConverter(PyObject* obj, void *_result);
+#endif
+
 #define _cleanup_Py_DECREF_ __attribute__((cleanup(cleanup_Py_DECREFp)))
 
 #if PY_MAJOR_VERSION >=3

commit 1070f974f7a1b6ba012e352b9d635d3902eca244
Author: Zbigniew Jędrzejewski-Szmek <zbyszek at in.waw.pl>
Date:   Wed Jul 17 12:50:43 2013 -0400

    systemd-python: fix iteration
    
    Back in 6a58bf4135 raising stop iteration was removed from the C
    code, but wasn't added in the Python counterpart.

diff --git a/configure.ac b/configure.ac
index 1cffbbb..9095be7 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1005,7 +1005,6 @@ AC_MSG_RESULT([
         nss-myhostname:          ${have_myhostname}
         gudev:                   ${enable_gudev}
         gintrospection:          ${enable_introspection}
-        keymap:                  ${enable_keymap}
         Python:                  ${have_python}
         Python Headers:          ${have_python_devel}
         man pages:               ${have_manpages}
diff --git a/src/python-systemd/journal.py b/src/python-systemd/journal.py
index 8fd1bb3..adcc844 100644
--- a/src/python-systemd/journal.py
+++ b/src/python-systemd/journal.py
@@ -191,18 +191,18 @@ class Reader(_Reader):
         """
         return self
 
-    if _sys.version_info >= (3,):
-        def __next__(self):
-            """Part of iterator protocol.
-            Returns self.get_next().
-            """
-            return self.get_next()
-    else:
-        def next(self):
-            """Part of iterator protocol.
-            Returns self.get_next().
-            """
-            return self.get_next()
+    def __next__(self):
+        """Part of iterator protocol.
+        Returns self.get_next() or raises StopIteration.
+        """
+        ans = self.get_next()
+        if ans:
+            return ans
+        else:
+            raise StopIteration()
+
+    if _sys.version_info < (3,):
+        next = __next__
 
     def add_match(self, *args, **kwargs):
         """Add one or more matches to the filter journal log entries.



More information about the systemd-commits mailing list