[systemd-commits] 13 commits - .gitignore Makefile.am TODO man/systemd.unit.xml src/core src/journal src/libsystemd-network src/network src/shared src/systemctl src/test src/udev

Lennart Poettering lennart at kemper.freedesktop.org
Thu Nov 6 05:31:23 PST 2014


 .gitignore                                |    2 
 Makefile.am                               |   34 -
 TODO                                      |    2 
 man/systemd.unit.xml                      |   51 ++
 src/core/condition.c                      |  267 ---------------
 src/core/condition.h                      |   26 -
 src/core/dbus-unit.c                      |   25 +
 src/core/job.c                            |    8 
 src/core/job.h                            |    1 
 src/core/load-fragment-gperf.gperf.m4     |   57 ++-
 src/core/load-fragment.c                  |  131 +++----
 src/core/unit.c                           |  100 +++++
 src/core/unit.h                           |    3 
 src/journal/journald-audit.c              |    4 
 src/libsystemd-network/network-internal.c |    8 
 src/libsystemd-network/network-internal.h |    2 
 src/network/networkd-netdev.h             |    1 
 src/network/networkd.h                    |    2 
 src/shared/architecture.c                 |    8 
 src/shared/architecture.h                 |   10 
 src/shared/condition-util.c               |  260 --------------
 src/shared/condition-util.h               |   81 ----
 src/shared/condition.c                    |  529 ++++++++++++++++++++++++++++++
 src/shared/condition.h                    |   96 +++++
 src/shared/util.c                         |   14 
 src/systemctl/systemctl.c                 |   61 +++
 src/test/test-architecture.c              |    3 
 src/test/test-condition-util.c            |  120 ------
 src/test/test-condition.c                 |  194 +++++++++++
 src/test/test-tables.c                    |    2 
 src/udev/net/link-config.h                |    5 
 31 files changed, 1188 insertions(+), 919 deletions(-)

New commits:
commit 7fd682bbebabe140e0d07a468e8f87cde380b859
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Nov 6 14:31:04 2014 +0100

    update TODO

diff --git a/TODO b/TODO
index 8339b93..9243b3d 100644
--- a/TODO
+++ b/TODO
@@ -85,8 +85,6 @@ Features:
 
 * maybe add "systemctl edit" that copies unit files from /usr/lib/systemd/system to /etc/systemd/system and invokes vim on them
 
-* maybe introduce AssertXYZ= similar to ConditionXYZ= that causes a unit to fail (instead of skipping it) if some condition is not true...
-
 * refcounting in sd-resolve is borked
 
 * exponential backoff in timesyncd and resolved when we cannot reach a server

commit 134e56dcc53970a20a858283650bb92cd5da1d17
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Nov 6 14:19:05 2014 +0100

    shared: rename condition-util.[ch] to condition.[ch]
    
    Now that we only have one file with condition implementations around, we
    can drop the -util suffix and simplify things a bit.

diff --git a/.gitignore b/.gitignore
index 6649d19..4c66564 100644
--- a/.gitignore
+++ b/.gitignore
@@ -157,7 +157,7 @@
 /test-cgroup-util
 /test-compress
 /test-compress-benchmark
-/test-condition-util
+/test-condition
 /test-conf-files
 /test-copy
 /test-coredump-vacuum
diff --git a/Makefile.am b/Makefile.am
index a10c306..461ffa9 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -861,8 +861,8 @@ libsystemd_shared_la_SOURCES = \
 	src/shared/audit.h \
 	src/shared/xml.c \
 	src/shared/xml.h \
-	src/shared/condition-util.c \
-	src/shared/condition-util.h \
+	src/shared/condition.c \
+	src/shared/condition.h \
 	src/shared/bus-label.c \
 	src/shared/bus-label.h \
 	src/shared/gpt.h \
@@ -1357,7 +1357,7 @@ tests += \
 	test-capability \
 	test-async \
 	test-ratelimit \
-	test-condition-util \
+	test-condition \
 	test-uid-range \
 	test-bus-policy \
 	test-locale-util \
@@ -1517,10 +1517,10 @@ test_copy_SOURCES = \
 test_copy_LDADD = \
 	libsystemd-shared.la
 
-test_condition_util_SOURCES = \
-	src/test/test-condition-util.c
+test_condition_SOURCES = \
+	src/test/test-condition.c
 
-test_condition_util_LDADD = \
+test_condition_LDADD = \
 	libsystemd-shared.la \
 	libsystemd-internal.la \
 	libsystemd-capability.la \
diff --git a/src/core/unit.h b/src/core/unit.h
index b5a224b..8b24272 100644
--- a/src/core/unit.h
+++ b/src/core/unit.h
@@ -38,7 +38,7 @@ typedef struct UnitStatusMessageFormats UnitStatusMessageFormats;
 #include "socket-util.h"
 #include "execute.h"
 #include "cgroup.h"
-#include "condition-util.h"
+#include "condition.h"
 #include "install.h"
 #include "unit-name.h"
 #include "failure-action.h"
diff --git a/src/libsystemd-network/network-internal.c b/src/libsystemd-network/network-internal.c
index f3141f7..6f16050 100644
--- a/src/libsystemd-network/network-internal.c
+++ b/src/libsystemd-network/network-internal.c
@@ -32,7 +32,7 @@
 #include "utf8.h"
 #include "util.h"
 #include "conf-parser.h"
-#include "condition-util.h"
+#include "condition.h"
 #include "network-internal.h"
 
 const char *net_get_name(struct udev_device *device) {
diff --git a/src/libsystemd-network/network-internal.h b/src/libsystemd-network/network-internal.h
index 49387d0..c64db2e 100644
--- a/src/libsystemd-network/network-internal.h
+++ b/src/libsystemd-network/network-internal.h
@@ -26,7 +26,7 @@
 #include <stdbool.h>
 
 #include "udev.h"
-#include "condition-util.h"
+#include "condition.h"
 
 bool net_match_config(const struct ether_addr *match_mac,
                       const char *match_path,
diff --git a/src/network/networkd-netdev.h b/src/network/networkd-netdev.h
index e9a8a16..a6fad2d 100644
--- a/src/network/networkd-netdev.h
+++ b/src/network/networkd-netdev.h
@@ -25,7 +25,6 @@
 #include "hashmap.h"
 #include "list.h"
 #include "set.h"
-#include "condition-util.h"
 #include "in-addr-util.h"
 
 typedef struct NetDevVTable NetDevVTable;
diff --git a/src/network/networkd.h b/src/network/networkd.h
index d4e79ab..19a661e 100644
--- a/src/network/networkd.h
+++ b/src/network/networkd.h
@@ -37,7 +37,7 @@
 #include "hashmap.h"
 #include "list.h"
 #include "set.h"
-#include "condition-util.h"
+#include "condition.h"
 #include "in-addr-util.h"
 
 #define CACHE_INFO_INFINITY_LIFE_TIME 0xFFFFFFFFU
diff --git a/src/shared/condition-util.c b/src/shared/condition-util.c
deleted file mode 100644
index 5db20a7..0000000
--- a/src/shared/condition-util.c
+++ /dev/null
@@ -1,529 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
-  This file is part of systemd.
-
-  Copyright 2010 Lennart Poettering
-
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU Lesser General Public License as published by
-  the Free Software Foundation; either version 2.1 of the License, or
-  (at your option) any later version.
-
-  systemd is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <stdlib.h>
-#include <errno.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/statvfs.h>
-#include <fnmatch.h>
-
-#include "sd-id128.h"
-#include "util.h"
-#include "condition-util.h"
-#include "virt.h"
-#include "path-util.h"
-#include "fileio.h"
-#include "unit.h"
-#include "architecture.h"
-#include "virt.h"
-#include "smack-util.h"
-#include "apparmor-util.h"
-#include "ima-util.h"
-#include "selinux-util.h"
-#include "audit.h"
-
-Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate) {
-        Condition *c;
-        int r;
-
-        assert(type >= 0);
-        assert(type < _CONDITION_TYPE_MAX);
-        assert(!parameter == (type == CONDITION_NULL));
-
-        c = new0(Condition, 1);
-        if (!c)
-                return NULL;
-
-        c->type = type;
-        c->trigger = trigger;
-        c->negate = negate;
-
-        r = free_and_strdup(&c->parameter, parameter);
-        if (r < 0) {
-                free(c);
-                return NULL;
-        }
-
-        return c;
-}
-
-void condition_free(Condition *c) {
-        assert(c);
-
-        free(c->parameter);
-        free(c);
-}
-
-void condition_free_list(Condition *first) {
-        Condition *c, *n;
-
-        LIST_FOREACH_SAFE(conditions, c, n, first)
-                condition_free(c);
-}
-
-static int condition_test_kernel_command_line(Condition *c) {
-        _cleanup_free_ char *line = NULL;
-        const char *p;
-        bool equal;
-        int r;
-
-        assert(c);
-        assert(c->parameter);
-        assert(c->type == CONDITION_KERNEL_COMMAND_LINE);
-
-        r = proc_cmdline(&line);
-        if (r < 0)
-                return r;
-        if (r == 0)
-                return false;
-
-        equal = !!strchr(c->parameter, '=');
-        p = line;
-
-        for (;;) {
-                _cleanup_free_ char *word = NULL;
-                bool found;
-
-                r = unquote_first_word(&p, &word);
-                if (r < 0)
-                        return r;
-                if (r == 0)
-                        break;
-
-                if (equal)
-                        found = streq(word, c->parameter);
-                else {
-                        const char *f;
-
-                        f = startswith(word, c->parameter);
-                        found = f && (*f == '=' || *f == 0);
-                }
-
-                if (found)
-                        return true;
-        }
-
-        return false;
-}
-
-static int condition_test_virtualization(Condition *c) {
-        int b, v;
-        const char *id;
-
-        assert(c);
-        assert(c->parameter);
-        assert(c->type == CONDITION_VIRTUALIZATION);
-
-        v = detect_virtualization(&id);
-        if (v < 0)
-                return v;
-
-        /* First, compare with yes/no */
-        b = parse_boolean(c->parameter);
-
-        if (v > 0 && b > 0)
-                return true;
-
-        if (v == 0 && b == 0)
-                return true;
-
-        /* Then, compare categorization */
-        if (v == VIRTUALIZATION_VM && streq(c->parameter, "vm"))
-                return true;
-
-        if (v == VIRTUALIZATION_CONTAINER && streq(c->parameter, "container"))
-                return true;
-
-        /* Finally compare id */
-        return v > 0 && streq(c->parameter, id);
-}
-
-static int condition_test_architecture(Condition *c) {
-        int a, b;
-
-        assert(c);
-        assert(c->parameter);
-        assert(c->type == CONDITION_ARCHITECTURE);
-
-        a = uname_architecture();
-        if (a < 0)
-                return a;
-
-        if (streq(c->parameter, "native"))
-                b = native_architecture();
-        else
-                b = architecture_from_string(c->parameter);
-        if (b < 0)
-                return b;
-
-        return a == b;
-}
-
-static int condition_test_host(Condition *c) {
-        _cleanup_free_ char *h = NULL;
-        sd_id128_t x, y;
-        int r;
-
-        assert(c);
-        assert(c->parameter);
-        assert(c->type == CONDITION_HOST);
-
-        if (sd_id128_from_string(c->parameter, &x) >= 0) {
-
-                r = sd_id128_get_machine(&y);
-                if (r < 0)
-                        return r;
-
-                return sd_id128_equal(x, y);
-        }
-
-        h = gethostname_malloc();
-        if (!h)
-                return -ENOMEM;
-
-        return fnmatch(c->parameter, h, FNM_CASEFOLD) == 0;
-}
-
-static int condition_test_ac_power(Condition *c) {
-        int r;
-
-        assert(c);
-        assert(c->parameter);
-        assert(c->type == CONDITION_AC_POWER);
-
-        r = parse_boolean(c->parameter);
-        if (r < 0)
-                return r;
-
-        return (on_ac_power() != 0) == !!r;
-}
-
-static int condition_test_security(Condition *c) {
-        assert(c);
-        assert(c->parameter);
-        assert(c->type == CONDITION_SECURITY);
-
-        if (streq(c->parameter, "selinux"))
-                return mac_selinux_use();
-        if (streq(c->parameter, "smack"))
-                return mac_smack_use();
-        if (streq(c->parameter, "apparmor"))
-                return mac_apparmor_use();
-        if (streq(c->parameter, "audit"))
-                return use_audit();
-        if (streq(c->parameter, "ima"))
-                return use_ima();
-
-        return false;
-}
-
-static int condition_test_capability(Condition *c) {
-        _cleanup_fclose_ FILE *f = NULL;
-        cap_value_t value;
-        char line[LINE_MAX];
-        unsigned long long capabilities = -1;
-
-        assert(c);
-        assert(c->parameter);
-        assert(c->type == CONDITION_CAPABILITY);
-
-        /* If it's an invalid capability, we don't have it */
-
-        if (cap_from_name(c->parameter, &value) < 0)
-                return -EINVAL;
-
-        /* If it's a valid capability we default to assume
-         * that we have it */
-
-        f = fopen("/proc/self/status", "re");
-        if (!f)
-                return -errno;
-
-        while (fgets(line, sizeof(line), f)) {
-                truncate_nl(line);
-
-                if (startswith(line, "CapBnd:")) {
-                        (void) sscanf(line+7, "%llx", &capabilities);
-                        break;
-                }
-        }
-
-        return !!(capabilities & (1ULL << value));
-}
-
-static int condition_test_needs_update(Condition *c) {
-        const char *p;
-        struct stat usr, other;
-
-        assert(c);
-        assert(c->parameter);
-        assert(c->type == CONDITION_NEEDS_UPDATE);
-
-        /* If the file system is read-only we shouldn't suggest an update */
-        if (path_is_read_only_fs(c->parameter) > 0)
-                return false;
-
-        /* Any other failure means we should allow the condition to be true,
-         * so that we rather invoke too many update tools then too
-         * few. */
-
-        if (!path_is_absolute(c->parameter))
-                return true;
-
-        p = strappenda(c->parameter, "/.updated");
-        if (lstat(p, &other) < 0)
-                return true;
-
-        if (lstat("/usr/", &usr) < 0)
-                return true;
-
-        return usr.st_mtim.tv_sec > other.st_mtim.tv_sec ||
-                (usr.st_mtim.tv_sec == other.st_mtim.tv_sec && usr.st_mtim.tv_nsec > other.st_mtim.tv_nsec);
-}
-
-static int condition_test_first_boot(Condition *c) {
-        int r;
-
-        assert(c);
-        assert(c->parameter);
-        assert(c->type == CONDITION_FIRST_BOOT);
-
-        r = parse_boolean(c->parameter);
-        if (r < 0)
-                return r;
-
-        return (access("/run/systemd/first-boot", F_OK) >= 0) == !!r;
-}
-
-static int condition_test_path_exists(Condition *c) {
-        assert(c);
-        assert(c->parameter);
-        assert(c->type == CONDITION_PATH_EXISTS);
-
-        return access(c->parameter, F_OK) >= 0;
-}
-
-static int condition_test_path_exists_glob(Condition *c) {
-        assert(c);
-        assert(c->parameter);
-        assert(c->type == CONDITION_PATH_EXISTS_GLOB);
-
-        return glob_exists(c->parameter) > 0;
-}
-
-static int condition_test_path_is_directory(Condition *c) {
-        assert(c);
-        assert(c->parameter);
-        assert(c->type == CONDITION_PATH_IS_DIRECTORY);
-
-        return is_dir(c->parameter, true) > 0;
-}
-
-static int condition_test_path_is_symbolic_link(Condition *c) {
-        assert(c);
-        assert(c->parameter);
-        assert(c->type == CONDITION_PATH_IS_SYMBOLIC_LINK);
-
-        return is_symlink(c->parameter) > 0;
-}
-
-static int condition_test_path_is_mount_point(Condition *c) {
-        assert(c);
-        assert(c->parameter);
-        assert(c->type == CONDITION_PATH_IS_MOUNT_POINT);
-
-        return path_is_mount_point(c->parameter, true) > 0;
-}
-
-static int condition_test_path_is_read_write(Condition *c) {
-        assert(c);
-        assert(c->parameter);
-        assert(c->type == CONDITION_PATH_IS_READ_WRITE);
-
-        return path_is_read_only_fs(c->parameter) <= 0;
-}
-
-static int condition_test_directory_not_empty(Condition *c) {
-        int r;
-
-        assert(c);
-        assert(c->parameter);
-        assert(c->type == CONDITION_DIRECTORY_NOT_EMPTY);
-
-        r = dir_is_empty(c->parameter);
-        return r <= 0 && r != -ENOENT;
-}
-
-static int condition_test_file_not_empty(Condition *c) {
-        struct stat st;
-
-        assert(c);
-        assert(c->parameter);
-        assert(c->type == CONDITION_FILE_NOT_EMPTY);
-
-        return (stat(c->parameter, &st) >= 0 &&
-                S_ISREG(st.st_mode) &&
-                st.st_size > 0);
-}
-
-static int condition_test_file_is_executable(Condition *c) {
-        struct stat st;
-
-        assert(c);
-        assert(c->parameter);
-        assert(c->type == CONDITION_FILE_IS_EXECUTABLE);
-
-        return (stat(c->parameter, &st) >= 0 &&
-                S_ISREG(st.st_mode) &&
-                (st.st_mode & 0111));
-}
-
-static int condition_test_null(Condition *c) {
-        assert(c);
-        assert(c->type == CONDITION_NULL);
-
-        /* Note that during parsing we already evaluate the string and
-         * store it in c->negate */
-        return true;
-}
-
-int condition_test(Condition *c) {
-
-        static int (*const condition_tests[_CONDITION_TYPE_MAX])(Condition *c) = {
-                [CONDITION_PATH_EXISTS] = condition_test_path_exists,
-                [CONDITION_PATH_EXISTS_GLOB] = condition_test_path_exists_glob,
-                [CONDITION_PATH_IS_DIRECTORY] = condition_test_path_is_directory,
-                [CONDITION_PATH_IS_SYMBOLIC_LINK] = condition_test_path_is_symbolic_link,
-                [CONDITION_PATH_IS_MOUNT_POINT] = condition_test_path_is_mount_point,
-                [CONDITION_PATH_IS_READ_WRITE] = condition_test_path_is_read_write,
-                [CONDITION_DIRECTORY_NOT_EMPTY] = condition_test_directory_not_empty,
-                [CONDITION_FILE_NOT_EMPTY] = condition_test_file_not_empty,
-                [CONDITION_FILE_IS_EXECUTABLE] = condition_test_file_is_executable,
-                [CONDITION_KERNEL_COMMAND_LINE] = condition_test_kernel_command_line,
-                [CONDITION_VIRTUALIZATION] = condition_test_virtualization,
-                [CONDITION_SECURITY] = condition_test_security,
-                [CONDITION_CAPABILITY] = condition_test_capability,
-                [CONDITION_HOST] = condition_test_host,
-                [CONDITION_AC_POWER] = condition_test_ac_power,
-                [CONDITION_ARCHITECTURE] = condition_test_architecture,
-                [CONDITION_NEEDS_UPDATE] = condition_test_needs_update,
-                [CONDITION_FIRST_BOOT] = condition_test_first_boot,
-                [CONDITION_NULL] = condition_test_null,
-        };
-
-        int r, b;
-
-        assert(c);
-        assert(c->type >= 0);
-        assert(c->type < _CONDITION_TYPE_MAX);
-
-        r = condition_tests[c->type](c);
-        if (r < 0) {
-                c->result = CONDITION_ERROR;
-                return r;
-        }
-
-        b = (r > 0) == !c->negate;
-        c->result = b ? CONDITION_SUCCEEDED : CONDITION_FAILED;
-        return b;
-}
-
-void condition_dump(Condition *c, FILE *f, const char *prefix, const char *(*to_string)(ConditionType t)) {
-        assert(c);
-        assert(f);
-
-        if (!prefix)
-                prefix = "";
-
-        fprintf(f,
-                "%s\t%s: %s%s%s %s\n",
-                prefix,
-                to_string(c->type),
-                c->trigger ? "|" : "",
-                c->negate ? "!" : "",
-                c->parameter,
-                condition_result_to_string(c->result));
-}
-
-void condition_dump_list(Condition *first, FILE *f, const char *prefix, const char *(*to_string)(ConditionType t)) {
-        Condition *c;
-
-        LIST_FOREACH(conditions, c, first)
-                condition_dump(c, f, prefix, to_string);
-}
-
-static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
-        [CONDITION_ARCHITECTURE] = "ConditionArchitecture",
-        [CONDITION_VIRTUALIZATION] = "ConditionVirtualization",
-        [CONDITION_HOST] = "ConditionHost",
-        [CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine",
-        [CONDITION_SECURITY] = "ConditionSecurity",
-        [CONDITION_CAPABILITY] = "ConditionCapability",
-        [CONDITION_AC_POWER] = "ConditionACPower",
-        [CONDITION_NEEDS_UPDATE] = "ConditionNeedsUpdate",
-        [CONDITION_FIRST_BOOT] = "ConditionFirstBoot",
-        [CONDITION_PATH_EXISTS] = "ConditionPathExists",
-        [CONDITION_PATH_EXISTS_GLOB] = "ConditionPathExistsGlob",
-        [CONDITION_PATH_IS_DIRECTORY] = "ConditionPathIsDirectory",
-        [CONDITION_PATH_IS_SYMBOLIC_LINK] = "ConditionPathIsSymbolicLink",
-        [CONDITION_PATH_IS_MOUNT_POINT] = "ConditionPathIsMountPoint",
-        [CONDITION_PATH_IS_READ_WRITE] = "ConditionPathIsReadWrite",
-        [CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty",
-        [CONDITION_FILE_NOT_EMPTY] = "ConditionFileNotEmpty",
-        [CONDITION_FILE_IS_EXECUTABLE] = "ConditionFileIsExecutable",
-        [CONDITION_NULL] = "ConditionNull"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType);
-
-static const char* const assert_type_table[_CONDITION_TYPE_MAX] = {
-        [CONDITION_ARCHITECTURE] = "AssertArchitecture",
-        [CONDITION_VIRTUALIZATION] = "AssertVirtualization",
-        [CONDITION_HOST] = "AssertHost",
-        [CONDITION_KERNEL_COMMAND_LINE] = "AssertKernelCommandLine",
-        [CONDITION_SECURITY] = "AssertSecurity",
-        [CONDITION_CAPABILITY] = "AssertCapability",
-        [CONDITION_AC_POWER] = "AssertACPower",
-        [CONDITION_NEEDS_UPDATE] = "AssertNeedsUpdate",
-        [CONDITION_FIRST_BOOT] = "AssertFirstBoot",
-        [CONDITION_PATH_EXISTS] = "AssertPathExists",
-        [CONDITION_PATH_EXISTS_GLOB] = "AssertPathExistsGlob",
-        [CONDITION_PATH_IS_DIRECTORY] = "AssertPathIsDirectory",
-        [CONDITION_PATH_IS_SYMBOLIC_LINK] = "AssertPathIsSymbolicLink",
-        [CONDITION_PATH_IS_MOUNT_POINT] = "AssertPathIsMountPoint",
-        [CONDITION_PATH_IS_READ_WRITE] = "AssertPathIsReadWrite",
-        [CONDITION_DIRECTORY_NOT_EMPTY] = "AssertDirectoryNotEmpty",
-        [CONDITION_FILE_NOT_EMPTY] = "AssertFileNotEmpty",
-        [CONDITION_FILE_IS_EXECUTABLE] = "AssertFileIsExecutable",
-        [CONDITION_NULL] = "AssertNull"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(assert_type, ConditionType);
-
-static const char* const condition_result_table[_CONDITION_RESULT_MAX] = {
-        [CONDITION_UNTESTED] = "untested",
-        [CONDITION_SUCCEEDED] = "succeeded",
-        [CONDITION_FAILED] = "failed",
-        [CONDITION_ERROR] = "error",
-};
-
-DEFINE_STRING_TABLE_LOOKUP(condition_result, ConditionResult);
diff --git a/src/shared/condition-util.h b/src/shared/condition-util.h
deleted file mode 100644
index 28d1d94..0000000
--- a/src/shared/condition-util.h
+++ /dev/null
@@ -1,96 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
-  This file is part of systemd.
-
-  Copyright 2010 Lennart Poettering
-
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU Lesser General Public License as published by
-  the Free Software Foundation; either version 2.1 of the License, or
-  (at your option) any later version.
-
-  systemd is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <stdbool.h>
-#include <stdio.h>
-
-#include "list.h"
-#include "macro.h"
-
-typedef enum ConditionType {
-        CONDITION_ARCHITECTURE,
-        CONDITION_VIRTUALIZATION,
-        CONDITION_HOST,
-        CONDITION_KERNEL_COMMAND_LINE,
-        CONDITION_SECURITY,
-        CONDITION_CAPABILITY,
-        CONDITION_AC_POWER,
-
-        CONDITION_NEEDS_UPDATE,
-        CONDITION_FIRST_BOOT,
-
-        CONDITION_PATH_EXISTS,
-        CONDITION_PATH_EXISTS_GLOB,
-        CONDITION_PATH_IS_DIRECTORY,
-        CONDITION_PATH_IS_SYMBOLIC_LINK,
-        CONDITION_PATH_IS_MOUNT_POINT,
-        CONDITION_PATH_IS_READ_WRITE,
-        CONDITION_DIRECTORY_NOT_EMPTY,
-        CONDITION_FILE_NOT_EMPTY,
-        CONDITION_FILE_IS_EXECUTABLE,
-
-        CONDITION_NULL,
-
-        _CONDITION_TYPE_MAX,
-        _CONDITION_TYPE_INVALID = -1
-} ConditionType;
-
-typedef enum ConditionResult {
-        CONDITION_UNTESTED,
-        CONDITION_SUCCEEDED,
-        CONDITION_FAILED,
-        CONDITION_ERROR,
-        _CONDITION_RESULT_MAX,
-        _CONDITION_RESULT_INVALID = -1
-} ConditionResult;
-
-typedef struct Condition {
-        ConditionType type:8;
-
-        bool trigger:1;
-        bool negate:1;
-
-        ConditionResult result:6;
-
-        char *parameter;
-
-        LIST_FIELDS(struct Condition, conditions);
-} Condition;
-
-Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate);
-void condition_free(Condition *c);
-void condition_free_list(Condition *c);
-
-int condition_test(Condition *c);
-
-void condition_dump(Condition *c, FILE *f, const char *prefix, const char *(*to_string)(ConditionType t));
-void condition_dump_list(Condition *c, FILE *f, const char *prefix, const char *(*to_string)(ConditionType t));
-
-const char* condition_type_to_string(ConditionType t) _const_;
-ConditionType condition_type_from_string(const char *s) _pure_;
-
-const char* assert_type_to_string(ConditionType t) _const_;
-ConditionType assert_type_from_string(const char *s) _pure_;
-
-const char* condition_result_to_string(ConditionResult r) _const_;
-ConditionResult condition_result_from_string(const char *s) _pure_;
diff --git a/src/shared/condition.c b/src/shared/condition.c
new file mode 100644
index 0000000..08bebee
--- /dev/null
+++ b/src/shared/condition.c
@@ -0,0 +1,529 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/statvfs.h>
+#include <fnmatch.h>
+
+#include "sd-id128.h"
+#include "util.h"
+#include "virt.h"
+#include "path-util.h"
+#include "fileio.h"
+#include "unit.h"
+#include "architecture.h"
+#include "virt.h"
+#include "smack-util.h"
+#include "apparmor-util.h"
+#include "ima-util.h"
+#include "selinux-util.h"
+#include "audit.h"
+#include "condition.h"
+
+Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate) {
+        Condition *c;
+        int r;
+
+        assert(type >= 0);
+        assert(type < _CONDITION_TYPE_MAX);
+        assert(!parameter == (type == CONDITION_NULL));
+
+        c = new0(Condition, 1);
+        if (!c)
+                return NULL;
+
+        c->type = type;
+        c->trigger = trigger;
+        c->negate = negate;
+
+        r = free_and_strdup(&c->parameter, parameter);
+        if (r < 0) {
+                free(c);
+                return NULL;
+        }
+
+        return c;
+}
+
+void condition_free(Condition *c) {
+        assert(c);
+
+        free(c->parameter);
+        free(c);
+}
+
+void condition_free_list(Condition *first) {
+        Condition *c, *n;
+
+        LIST_FOREACH_SAFE(conditions, c, n, first)
+                condition_free(c);
+}
+
+static int condition_test_kernel_command_line(Condition *c) {
+        _cleanup_free_ char *line = NULL;
+        const char *p;
+        bool equal;
+        int r;
+
+        assert(c);
+        assert(c->parameter);
+        assert(c->type == CONDITION_KERNEL_COMMAND_LINE);
+
+        r = proc_cmdline(&line);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return false;
+
+        equal = !!strchr(c->parameter, '=');
+        p = line;
+
+        for (;;) {
+                _cleanup_free_ char *word = NULL;
+                bool found;
+
+                r = unquote_first_word(&p, &word);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        break;
+
+                if (equal)
+                        found = streq(word, c->parameter);
+                else {
+                        const char *f;
+
+                        f = startswith(word, c->parameter);
+                        found = f && (*f == '=' || *f == 0);
+                }
+
+                if (found)
+                        return true;
+        }
+
+        return false;
+}
+
+static int condition_test_virtualization(Condition *c) {
+        int b, v;
+        const char *id;
+
+        assert(c);
+        assert(c->parameter);
+        assert(c->type == CONDITION_VIRTUALIZATION);
+
+        v = detect_virtualization(&id);
+        if (v < 0)
+                return v;
+
+        /* First, compare with yes/no */
+        b = parse_boolean(c->parameter);
+
+        if (v > 0 && b > 0)
+                return true;
+
+        if (v == 0 && b == 0)
+                return true;
+
+        /* Then, compare categorization */
+        if (v == VIRTUALIZATION_VM && streq(c->parameter, "vm"))
+                return true;
+
+        if (v == VIRTUALIZATION_CONTAINER && streq(c->parameter, "container"))
+                return true;
+
+        /* Finally compare id */
+        return v > 0 && streq(c->parameter, id);
+}
+
+static int condition_test_architecture(Condition *c) {
+        int a, b;
+
+        assert(c);
+        assert(c->parameter);
+        assert(c->type == CONDITION_ARCHITECTURE);
+
+        a = uname_architecture();
+        if (a < 0)
+                return a;
+
+        if (streq(c->parameter, "native"))
+                b = native_architecture();
+        else
+                b = architecture_from_string(c->parameter);
+        if (b < 0)
+                return b;
+
+        return a == b;
+}
+
+static int condition_test_host(Condition *c) {
+        _cleanup_free_ char *h = NULL;
+        sd_id128_t x, y;
+        int r;
+
+        assert(c);
+        assert(c->parameter);
+        assert(c->type == CONDITION_HOST);
+
+        if (sd_id128_from_string(c->parameter, &x) >= 0) {
+
+                r = sd_id128_get_machine(&y);
+                if (r < 0)
+                        return r;
+
+                return sd_id128_equal(x, y);
+        }
+
+        h = gethostname_malloc();
+        if (!h)
+                return -ENOMEM;
+
+        return fnmatch(c->parameter, h, FNM_CASEFOLD) == 0;
+}
+
+static int condition_test_ac_power(Condition *c) {
+        int r;
+
+        assert(c);
+        assert(c->parameter);
+        assert(c->type == CONDITION_AC_POWER);
+
+        r = parse_boolean(c->parameter);
+        if (r < 0)
+                return r;
+
+        return (on_ac_power() != 0) == !!r;
+}
+
+static int condition_test_security(Condition *c) {
+        assert(c);
+        assert(c->parameter);
+        assert(c->type == CONDITION_SECURITY);
+
+        if (streq(c->parameter, "selinux"))
+                return mac_selinux_use();
+        if (streq(c->parameter, "smack"))
+                return mac_smack_use();
+        if (streq(c->parameter, "apparmor"))
+                return mac_apparmor_use();
+        if (streq(c->parameter, "audit"))
+                return use_audit();
+        if (streq(c->parameter, "ima"))
+                return use_ima();
+
+        return false;
+}
+
+static int condition_test_capability(Condition *c) {
+        _cleanup_fclose_ FILE *f = NULL;
+        cap_value_t value;
+        char line[LINE_MAX];
+        unsigned long long capabilities = -1;
+
+        assert(c);
+        assert(c->parameter);
+        assert(c->type == CONDITION_CAPABILITY);
+
+        /* If it's an invalid capability, we don't have it */
+
+        if (cap_from_name(c->parameter, &value) < 0)
+                return -EINVAL;
+
+        /* If it's a valid capability we default to assume
+         * that we have it */
+
+        f = fopen("/proc/self/status", "re");
+        if (!f)
+                return -errno;
+
+        while (fgets(line, sizeof(line), f)) {
+                truncate_nl(line);
+
+                if (startswith(line, "CapBnd:")) {
+                        (void) sscanf(line+7, "%llx", &capabilities);
+                        break;
+                }
+        }
+
+        return !!(capabilities & (1ULL << value));
+}
+
+static int condition_test_needs_update(Condition *c) {
+        const char *p;
+        struct stat usr, other;
+
+        assert(c);
+        assert(c->parameter);
+        assert(c->type == CONDITION_NEEDS_UPDATE);
+
+        /* If the file system is read-only we shouldn't suggest an update */
+        if (path_is_read_only_fs(c->parameter) > 0)
+                return false;
+
+        /* Any other failure means we should allow the condition to be true,
+         * so that we rather invoke too many update tools then too
+         * few. */
+
+        if (!path_is_absolute(c->parameter))
+                return true;
+
+        p = strappenda(c->parameter, "/.updated");
+        if (lstat(p, &other) < 0)
+                return true;
+
+        if (lstat("/usr/", &usr) < 0)
+                return true;
+
+        return usr.st_mtim.tv_sec > other.st_mtim.tv_sec ||
+                (usr.st_mtim.tv_sec == other.st_mtim.tv_sec && usr.st_mtim.tv_nsec > other.st_mtim.tv_nsec);
+}
+
+static int condition_test_first_boot(Condition *c) {
+        int r;
+
+        assert(c);
+        assert(c->parameter);
+        assert(c->type == CONDITION_FIRST_BOOT);
+
+        r = parse_boolean(c->parameter);
+        if (r < 0)
+                return r;
+
+        return (access("/run/systemd/first-boot", F_OK) >= 0) == !!r;
+}
+
+static int condition_test_path_exists(Condition *c) {
+        assert(c);
+        assert(c->parameter);
+        assert(c->type == CONDITION_PATH_EXISTS);
+
+        return access(c->parameter, F_OK) >= 0;
+}
+
+static int condition_test_path_exists_glob(Condition *c) {
+        assert(c);
+        assert(c->parameter);
+        assert(c->type == CONDITION_PATH_EXISTS_GLOB);
+
+        return glob_exists(c->parameter) > 0;
+}
+
+static int condition_test_path_is_directory(Condition *c) {
+        assert(c);
+        assert(c->parameter);
+        assert(c->type == CONDITION_PATH_IS_DIRECTORY);
+
+        return is_dir(c->parameter, true) > 0;
+}
+
+static int condition_test_path_is_symbolic_link(Condition *c) {
+        assert(c);
+        assert(c->parameter);
+        assert(c->type == CONDITION_PATH_IS_SYMBOLIC_LINK);
+
+        return is_symlink(c->parameter) > 0;
+}
+
+static int condition_test_path_is_mount_point(Condition *c) {
+        assert(c);
+        assert(c->parameter);
+        assert(c->type == CONDITION_PATH_IS_MOUNT_POINT);
+
+        return path_is_mount_point(c->parameter, true) > 0;
+}
+
+static int condition_test_path_is_read_write(Condition *c) {
+        assert(c);
+        assert(c->parameter);
+        assert(c->type == CONDITION_PATH_IS_READ_WRITE);
+
+        return path_is_read_only_fs(c->parameter) <= 0;
+}
+
+static int condition_test_directory_not_empty(Condition *c) {
+        int r;
+
+        assert(c);
+        assert(c->parameter);
+        assert(c->type == CONDITION_DIRECTORY_NOT_EMPTY);
+
+        r = dir_is_empty(c->parameter);
+        return r <= 0 && r != -ENOENT;
+}
+
+static int condition_test_file_not_empty(Condition *c) {
+        struct stat st;
+
+        assert(c);
+        assert(c->parameter);
+        assert(c->type == CONDITION_FILE_NOT_EMPTY);
+
+        return (stat(c->parameter, &st) >= 0 &&
+                S_ISREG(st.st_mode) &&
+                st.st_size > 0);
+}
+
+static int condition_test_file_is_executable(Condition *c) {
+        struct stat st;
+
+        assert(c);
+        assert(c->parameter);
+        assert(c->type == CONDITION_FILE_IS_EXECUTABLE);
+
+        return (stat(c->parameter, &st) >= 0 &&
+                S_ISREG(st.st_mode) &&
+                (st.st_mode & 0111));
+}
+
+static int condition_test_null(Condition *c) {
+        assert(c);
+        assert(c->type == CONDITION_NULL);
+
+        /* Note that during parsing we already evaluate the string and
+         * store it in c->negate */
+        return true;
+}
+
+int condition_test(Condition *c) {
+
+        static int (*const condition_tests[_CONDITION_TYPE_MAX])(Condition *c) = {
+                [CONDITION_PATH_EXISTS] = condition_test_path_exists,
+                [CONDITION_PATH_EXISTS_GLOB] = condition_test_path_exists_glob,
+                [CONDITION_PATH_IS_DIRECTORY] = condition_test_path_is_directory,
+                [CONDITION_PATH_IS_SYMBOLIC_LINK] = condition_test_path_is_symbolic_link,
+                [CONDITION_PATH_IS_MOUNT_POINT] = condition_test_path_is_mount_point,
+                [CONDITION_PATH_IS_READ_WRITE] = condition_test_path_is_read_write,
+                [CONDITION_DIRECTORY_NOT_EMPTY] = condition_test_directory_not_empty,
+                [CONDITION_FILE_NOT_EMPTY] = condition_test_file_not_empty,
+                [CONDITION_FILE_IS_EXECUTABLE] = condition_test_file_is_executable,
+                [CONDITION_KERNEL_COMMAND_LINE] = condition_test_kernel_command_line,
+                [CONDITION_VIRTUALIZATION] = condition_test_virtualization,
+                [CONDITION_SECURITY] = condition_test_security,
+                [CONDITION_CAPABILITY] = condition_test_capability,
+                [CONDITION_HOST] = condition_test_host,
+                [CONDITION_AC_POWER] = condition_test_ac_power,
+                [CONDITION_ARCHITECTURE] = condition_test_architecture,
+                [CONDITION_NEEDS_UPDATE] = condition_test_needs_update,
+                [CONDITION_FIRST_BOOT] = condition_test_first_boot,
+                [CONDITION_NULL] = condition_test_null,
+        };
+
+        int r, b;
+
+        assert(c);
+        assert(c->type >= 0);
+        assert(c->type < _CONDITION_TYPE_MAX);
+
+        r = condition_tests[c->type](c);
+        if (r < 0) {
+                c->result = CONDITION_ERROR;
+                return r;
+        }
+
+        b = (r > 0) == !c->negate;
+        c->result = b ? CONDITION_SUCCEEDED : CONDITION_FAILED;
+        return b;
+}
+
+void condition_dump(Condition *c, FILE *f, const char *prefix, const char *(*to_string)(ConditionType t)) {
+        assert(c);
+        assert(f);
+
+        if (!prefix)
+                prefix = "";
+
+        fprintf(f,
+                "%s\t%s: %s%s%s %s\n",
+                prefix,
+                to_string(c->type),
+                c->trigger ? "|" : "",
+                c->negate ? "!" : "",
+                c->parameter,
+                condition_result_to_string(c->result));
+}
+
+void condition_dump_list(Condition *first, FILE *f, const char *prefix, const char *(*to_string)(ConditionType t)) {
+        Condition *c;
+
+        LIST_FOREACH(conditions, c, first)
+                condition_dump(c, f, prefix, to_string);
+}
+
+static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
+        [CONDITION_ARCHITECTURE] = "ConditionArchitecture",
+        [CONDITION_VIRTUALIZATION] = "ConditionVirtualization",
+        [CONDITION_HOST] = "ConditionHost",
+        [CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine",
+        [CONDITION_SECURITY] = "ConditionSecurity",
+        [CONDITION_CAPABILITY] = "ConditionCapability",
+        [CONDITION_AC_POWER] = "ConditionACPower",
+        [CONDITION_NEEDS_UPDATE] = "ConditionNeedsUpdate",
+        [CONDITION_FIRST_BOOT] = "ConditionFirstBoot",
+        [CONDITION_PATH_EXISTS] = "ConditionPathExists",
+        [CONDITION_PATH_EXISTS_GLOB] = "ConditionPathExistsGlob",
+        [CONDITION_PATH_IS_DIRECTORY] = "ConditionPathIsDirectory",
+        [CONDITION_PATH_IS_SYMBOLIC_LINK] = "ConditionPathIsSymbolicLink",
+        [CONDITION_PATH_IS_MOUNT_POINT] = "ConditionPathIsMountPoint",
+        [CONDITION_PATH_IS_READ_WRITE] = "ConditionPathIsReadWrite",
+        [CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty",
+        [CONDITION_FILE_NOT_EMPTY] = "ConditionFileNotEmpty",
+        [CONDITION_FILE_IS_EXECUTABLE] = "ConditionFileIsExecutable",
+        [CONDITION_NULL] = "ConditionNull"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType);
+
+static const char* const assert_type_table[_CONDITION_TYPE_MAX] = {
+        [CONDITION_ARCHITECTURE] = "AssertArchitecture",
+        [CONDITION_VIRTUALIZATION] = "AssertVirtualization",
+        [CONDITION_HOST] = "AssertHost",
+        [CONDITION_KERNEL_COMMAND_LINE] = "AssertKernelCommandLine",
+        [CONDITION_SECURITY] = "AssertSecurity",
+        [CONDITION_CAPABILITY] = "AssertCapability",
+        [CONDITION_AC_POWER] = "AssertACPower",
+        [CONDITION_NEEDS_UPDATE] = "AssertNeedsUpdate",
+        [CONDITION_FIRST_BOOT] = "AssertFirstBoot",
+        [CONDITION_PATH_EXISTS] = "AssertPathExists",
+        [CONDITION_PATH_EXISTS_GLOB] = "AssertPathExistsGlob",
+        [CONDITION_PATH_IS_DIRECTORY] = "AssertPathIsDirectory",
+        [CONDITION_PATH_IS_SYMBOLIC_LINK] = "AssertPathIsSymbolicLink",
+        [CONDITION_PATH_IS_MOUNT_POINT] = "AssertPathIsMountPoint",
+        [CONDITION_PATH_IS_READ_WRITE] = "AssertPathIsReadWrite",
+        [CONDITION_DIRECTORY_NOT_EMPTY] = "AssertDirectoryNotEmpty",
+        [CONDITION_FILE_NOT_EMPTY] = "AssertFileNotEmpty",
+        [CONDITION_FILE_IS_EXECUTABLE] = "AssertFileIsExecutable",
+        [CONDITION_NULL] = "AssertNull"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(assert_type, ConditionType);
+
+static const char* const condition_result_table[_CONDITION_RESULT_MAX] = {
+        [CONDITION_UNTESTED] = "untested",
+        [CONDITION_SUCCEEDED] = "succeeded",
+        [CONDITION_FAILED] = "failed",
+        [CONDITION_ERROR] = "error",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(condition_result, ConditionResult);
diff --git a/src/shared/condition.h b/src/shared/condition.h
new file mode 100644
index 0000000..28d1d94
--- /dev/null
+++ b/src/shared/condition.h
@@ -0,0 +1,96 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdbool.h>
+#include <stdio.h>
+
+#include "list.h"
+#include "macro.h"
+
+typedef enum ConditionType {
+        CONDITION_ARCHITECTURE,
+        CONDITION_VIRTUALIZATION,
+        CONDITION_HOST,
+        CONDITION_KERNEL_COMMAND_LINE,
+        CONDITION_SECURITY,
+        CONDITION_CAPABILITY,
+        CONDITION_AC_POWER,
+
+        CONDITION_NEEDS_UPDATE,
+        CONDITION_FIRST_BOOT,
+
+        CONDITION_PATH_EXISTS,
+        CONDITION_PATH_EXISTS_GLOB,
+        CONDITION_PATH_IS_DIRECTORY,
+        CONDITION_PATH_IS_SYMBOLIC_LINK,
+        CONDITION_PATH_IS_MOUNT_POINT,
+        CONDITION_PATH_IS_READ_WRITE,
+        CONDITION_DIRECTORY_NOT_EMPTY,
+        CONDITION_FILE_NOT_EMPTY,
+        CONDITION_FILE_IS_EXECUTABLE,
+
+        CONDITION_NULL,
+
+        _CONDITION_TYPE_MAX,
+        _CONDITION_TYPE_INVALID = -1
+} ConditionType;
+
+typedef enum ConditionResult {
+        CONDITION_UNTESTED,
+        CONDITION_SUCCEEDED,
+        CONDITION_FAILED,
+        CONDITION_ERROR,
+        _CONDITION_RESULT_MAX,
+        _CONDITION_RESULT_INVALID = -1
+} ConditionResult;
+
+typedef struct Condition {
+        ConditionType type:8;
+
+        bool trigger:1;
+        bool negate:1;
+
+        ConditionResult result:6;
+
+        char *parameter;
+
+        LIST_FIELDS(struct Condition, conditions);
+} Condition;
+
+Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate);
+void condition_free(Condition *c);
+void condition_free_list(Condition *c);
+
+int condition_test(Condition *c);
+
+void condition_dump(Condition *c, FILE *f, const char *prefix, const char *(*to_string)(ConditionType t));
+void condition_dump_list(Condition *c, FILE *f, const char *prefix, const char *(*to_string)(ConditionType t));
+
+const char* condition_type_to_string(ConditionType t) _const_;
+ConditionType condition_type_from_string(const char *s) _pure_;
+
+const char* assert_type_to_string(ConditionType t) _const_;
+ConditionType assert_type_from_string(const char *s) _pure_;
+
+const char* condition_result_to_string(ConditionResult r) _const_;
+ConditionResult condition_result_from_string(const char *s) _pure_;
diff --git a/src/test/test-condition-util.c b/src/test/test-condition-util.c
deleted file mode 100644
index b69e62b..0000000
--- a/src/test/test-condition-util.c
+++ /dev/null
@@ -1,194 +0,0 @@
-/***
-  This file is part of systemd
-
-  Copyright 2014 Ronny Chevalier
-
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU Lesser General Public License as published by
-  the Free Software Foundation; either version 2.1 of the License, or
-  (at your option) any later version.
-
-  systemd is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "condition-util.h"
-#include "macro.h"
-#include "util.h"
-#include "log.h"
-#include "architecture.h"
-#include "sd-id128.h"
-
-static void test_condition_test_path_exists(void) {
-        Condition *condition;
-
-        condition = condition_new(CONDITION_PATH_EXISTS, "/bin/sh", false, false);
-        assert_se(condition_test(condition));
-        condition_free(condition);
-
-        condition = condition_new(CONDITION_PATH_EXISTS, "/bin/s?", false, false);
-        assert_se(!condition_test(condition));
-        condition_free(condition);
-
-        condition = condition_new(CONDITION_PATH_EXISTS_GLOB, "/bin/s?", false, false);
-        assert_se(condition_test(condition));
-        condition_free(condition);
-
-        condition = condition_new(CONDITION_PATH_EXISTS_GLOB, "/bin/s?", false, true);
-        assert_se(!condition_test(condition));
-        condition_free(condition);
-
-        condition = condition_new(CONDITION_PATH_EXISTS, "/thiscertainlywontexist", false, false);
-        assert_se(!condition_test(condition));
-        condition_free(condition);
-
-        condition = condition_new(CONDITION_PATH_EXISTS, "/thiscertainlywontexist", false, true);
-        assert_se(condition_test(condition));
-        condition_free(condition);
-
-        condition = condition_new(CONDITION_PATH_IS_DIRECTORY, "/bin", false, false);
-        assert_se(condition_test(condition));
-        condition_free(condition);
-
-        condition = condition_new(CONDITION_DIRECTORY_NOT_EMPTY, "/bin", false, false);
-        assert_se(condition_test(condition));
-        condition_free(condition);
-
-        condition = condition_new(CONDITION_FILE_NOT_EMPTY, "/bin/sh", false, false);
-        assert_se(condition_test(condition));
-        condition_free(condition);
-
-        condition = condition_new(CONDITION_FILE_IS_EXECUTABLE, "/bin/sh", false, false);
-        assert_se(condition_test(condition));
-        condition_free(condition);
-
-        condition = condition_new(CONDITION_FILE_IS_EXECUTABLE, "/etc/passwd", false, false);
-        assert_se(!condition_test(condition));
-        condition_free(condition);
-
-        condition = condition_new(CONDITION_PATH_IS_MOUNT_POINT, "/proc", false, false);
-        assert_se(condition_test(condition));
-        condition_free(condition);
-
-        condition = condition_new(CONDITION_PATH_IS_MOUNT_POINT, "/", false, false);
-        assert_se(condition_test(condition));
-        condition_free(condition);
-
-        condition = condition_new(CONDITION_PATH_IS_MOUNT_POINT, "/bin", false, false);
-        assert_se(!condition_test(condition));
-        condition_free(condition);
-}
-
-static void test_condition_test_ac_power(void) {
-        Condition *condition;
-
-        condition = condition_new(CONDITION_AC_POWER, "true", false, false);
-        assert_se(condition_test(condition) == on_ac_power());
-        condition_free(condition);
-
-        condition = condition_new(CONDITION_AC_POWER, "false", false, false);
-        assert_se(condition_test(condition) != on_ac_power());
-        condition_free(condition);
-
-        condition = condition_new(CONDITION_AC_POWER, "false", false, true);
-        assert_se(condition_test(condition) == on_ac_power());
-        condition_free(condition);
-}
-
-static void test_condition_test_host(void) {
-        Condition *condition;
-        sd_id128_t id;
-        int r;
-        char sid[SD_ID128_STRING_MAX];
-        _cleanup_free_ char *hostname = NULL;
-
-        r = sd_id128_get_machine(&id);
-        assert_se(r >= 0);
-        assert_se(sd_id128_to_string(id, sid));
-
-        condition = condition_new(CONDITION_HOST, sid, false, false);
-        assert_se(condition_test(condition));
-        condition_free(condition);
-
-        condition = condition_new(CONDITION_HOST, "garbage value jjjjjjjjjjjjjj", false, false);
-        assert_se(!condition_test(condition));
-        condition_free(condition);
-
-        condition = condition_new(CONDITION_HOST, sid, false, true);
-        assert_se(!condition_test(condition));
-        condition_free(condition);
-
-        hostname = gethostname_malloc();
-        assert_se(hostname);
-
-        condition = condition_new(CONDITION_HOST, hostname, false, false);
-        assert_se(condition_test(condition));
-        condition_free(condition);
-}
-
-static void test_condition_test_architecture(void) {
-        Condition *condition;
-        const char *sa;
-        int a;
-
-        a = uname_architecture();
-        assert_se(a >= 0);
-
-        sa = architecture_to_string(a);
-        assert_se(sa);
-
-        condition = condition_new(CONDITION_ARCHITECTURE, sa, false, false);
-        assert_se(condition_test(condition));
-        condition_free(condition);
-
-        condition = condition_new(CONDITION_ARCHITECTURE, "garbage value", false, false);
-        assert_se(condition_test(condition) < 0);
-        condition_free(condition);
-
-        condition = condition_new(CONDITION_ARCHITECTURE, sa, false, true);
-        assert_se(!condition_test(condition));
-        condition_free(condition);
-}
-
-static void test_condition_test_kernel_command_line(void) {
-        Condition *condition;
-
-        condition = condition_new(CONDITION_KERNEL_COMMAND_LINE, "thisreallyshouldntbeonthekernelcommandline", false, false);
-        assert_se(!condition_test(condition));
-        condition_free(condition);
-
-        condition = condition_new(CONDITION_KERNEL_COMMAND_LINE, "andthis=neither", false, false);
-        assert_se(!condition_test(condition));
-        condition_free(condition);
-}
-
-static void test_condition_test_null(void) {
-        Condition *condition;
-
-        condition = condition_new(CONDITION_NULL, NULL, false, false);
-        assert_se(condition_test(condition));
-        condition_free(condition);
-
-        condition = condition_new(CONDITION_NULL, NULL, false, true);
-        assert_se(!condition_test(condition));
-        condition_free(condition);
-}
-
-int main(int argc, char *argv[]) {
-        log_parse_environment();
-        log_open();
-
-        test_condition_test_path_exists();
-        test_condition_test_ac_power();
-        test_condition_test_host();
-        test_condition_test_architecture();
-        test_condition_test_kernel_command_line();
-        test_condition_test_null();
-
-        return 0;
-}
diff --git a/src/test/test-condition.c b/src/test/test-condition.c
new file mode 100644
index 0000000..349c647
--- /dev/null
+++ b/src/test/test-condition.c
@@ -0,0 +1,194 @@
+/***
+  This file is part of systemd
+
+  Copyright 2014 Ronny Chevalier
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "condition.h"
+#include "macro.h"
+#include "util.h"
+#include "log.h"
+#include "architecture.h"
+#include "sd-id128.h"
+
+static void test_condition_test_path_exists(void) {
+        Condition *condition;
+
+        condition = condition_new(CONDITION_PATH_EXISTS, "/bin/sh", false, false);
+        assert_se(condition_test(condition));
+        condition_free(condition);
+
+        condition = condition_new(CONDITION_PATH_EXISTS, "/bin/s?", false, false);
+        assert_se(!condition_test(condition));
+        condition_free(condition);
+
+        condition = condition_new(CONDITION_PATH_EXISTS_GLOB, "/bin/s?", false, false);
+        assert_se(condition_test(condition));
+        condition_free(condition);
+
+        condition = condition_new(CONDITION_PATH_EXISTS_GLOB, "/bin/s?", false, true);
+        assert_se(!condition_test(condition));
+        condition_free(condition);
+
+        condition = condition_new(CONDITION_PATH_EXISTS, "/thiscertainlywontexist", false, false);
+        assert_se(!condition_test(condition));
+        condition_free(condition);
+
+        condition = condition_new(CONDITION_PATH_EXISTS, "/thiscertainlywontexist", false, true);
+        assert_se(condition_test(condition));
+        condition_free(condition);
+
+        condition = condition_new(CONDITION_PATH_IS_DIRECTORY, "/bin", false, false);
+        assert_se(condition_test(condition));
+        condition_free(condition);
+
+        condition = condition_new(CONDITION_DIRECTORY_NOT_EMPTY, "/bin", false, false);
+        assert_se(condition_test(condition));
+        condition_free(condition);
+
+        condition = condition_new(CONDITION_FILE_NOT_EMPTY, "/bin/sh", false, false);
+        assert_se(condition_test(condition));
+        condition_free(condition);
+
+        condition = condition_new(CONDITION_FILE_IS_EXECUTABLE, "/bin/sh", false, false);
+        assert_se(condition_test(condition));
+        condition_free(condition);
+
+        condition = condition_new(CONDITION_FILE_IS_EXECUTABLE, "/etc/passwd", false, false);
+        assert_se(!condition_test(condition));
+        condition_free(condition);
+
+        condition = condition_new(CONDITION_PATH_IS_MOUNT_POINT, "/proc", false, false);
+        assert_se(condition_test(condition));
+        condition_free(condition);
+
+        condition = condition_new(CONDITION_PATH_IS_MOUNT_POINT, "/", false, false);
+        assert_se(condition_test(condition));
+        condition_free(condition);
+
+        condition = condition_new(CONDITION_PATH_IS_MOUNT_POINT, "/bin", false, false);
+        assert_se(!condition_test(condition));
+        condition_free(condition);
+}
+
+static void test_condition_test_ac_power(void) {
+        Condition *condition;
+
+        condition = condition_new(CONDITION_AC_POWER, "true", false, false);
+        assert_se(condition_test(condition) == on_ac_power());
+        condition_free(condition);
+
+        condition = condition_new(CONDITION_AC_POWER, "false", false, false);
+        assert_se(condition_test(condition) != on_ac_power());
+        condition_free(condition);
+
+        condition = condition_new(CONDITION_AC_POWER, "false", false, true);
+        assert_se(condition_test(condition) == on_ac_power());
+        condition_free(condition);
+}
+
+static void test_condition_test_host(void) {
+        Condition *condition;
+        sd_id128_t id;
+        int r;
+        char sid[SD_ID128_STRING_MAX];
+        _cleanup_free_ char *hostname = NULL;
+
+        r = sd_id128_get_machine(&id);
+        assert_se(r >= 0);
+        assert_se(sd_id128_to_string(id, sid));
+
+        condition = condition_new(CONDITION_HOST, sid, false, false);
+        assert_se(condition_test(condition));
+        condition_free(condition);
+
+        condition = condition_new(CONDITION_HOST, "garbage value jjjjjjjjjjjjjj", false, false);
+        assert_se(!condition_test(condition));
+        condition_free(condition);
+
+        condition = condition_new(CONDITION_HOST, sid, false, true);
+        assert_se(!condition_test(condition));
+        condition_free(condition);
+
+        hostname = gethostname_malloc();
+        assert_se(hostname);
+
+        condition = condition_new(CONDITION_HOST, hostname, false, false);
+        assert_se(condition_test(condition));
+        condition_free(condition);
+}
+
+static void test_condition_test_architecture(void) {
+        Condition *condition;
+        const char *sa;
+        int a;
+
+        a = uname_architecture();
+        assert_se(a >= 0);
+
+        sa = architecture_to_string(a);
+        assert_se(sa);
+
+        condition = condition_new(CONDITION_ARCHITECTURE, sa, false, false);
+        assert_se(condition_test(condition));
+        condition_free(condition);
+
+        condition = condition_new(CONDITION_ARCHITECTURE, "garbage value", false, false);
+        assert_se(condition_test(condition) < 0);
+        condition_free(condition);
+
+        condition = condition_new(CONDITION_ARCHITECTURE, sa, false, true);
+        assert_se(!condition_test(condition));
+        condition_free(condition);
+}
+
+static void test_condition_test_kernel_command_line(void) {
+        Condition *condition;
+
+        condition = condition_new(CONDITION_KERNEL_COMMAND_LINE, "thisreallyshouldntbeonthekernelcommandline", false, false);
+        assert_se(!condition_test(condition));
+        condition_free(condition);
+
+        condition = condition_new(CONDITION_KERNEL_COMMAND_LINE, "andthis=neither", false, false);
+        assert_se(!condition_test(condition));
+        condition_free(condition);
+}
+
+static void test_condition_test_null(void) {
+        Condition *condition;
+
+        condition = condition_new(CONDITION_NULL, NULL, false, false);
+        assert_se(condition_test(condition));
+        condition_free(condition);
+
+        condition = condition_new(CONDITION_NULL, NULL, false, true);
+        assert_se(!condition_test(condition));
+        condition_free(condition);
+}
+
+int main(int argc, char *argv[]) {
+        log_parse_environment();
+        log_open();
+
+        test_condition_test_path_exists();
+        test_condition_test_ac_power();
+        test_condition_test_host();
+        test_condition_test_architecture();
+        test_condition_test_kernel_command_line();
+        test_condition_test_null();
+
+        return 0;
+}
diff --git a/src/test/test-tables.c b/src/test/test-tables.c
index d0a621c..97d5609 100644
--- a/src/test/test-tables.c
+++ b/src/test/test-tables.c
@@ -20,7 +20,7 @@
 #include "automount.h"
 #include "cgroup.h"
 #include "compress.h"
-#include "condition-util.h"
+#include "condition.h"
 #include "device.h"
 #include "execute.h"
 #include "exit-status.h"
diff --git a/src/udev/net/link-config.h b/src/udev/net/link-config.h
index 5f3d4ad..844ea98 100644
--- a/src/udev/net/link-config.h
+++ b/src/udev/net/link-config.h
@@ -22,11 +22,10 @@
 #pragma once
 
 #include "ethtool-util.h"
-
-#include "condition-util.h"
-#include "libudev.h"
+#include "condition.h"
 #include "util.h"
 #include "list.h"
+#include "libudev.h"
 
 typedef struct link_config_ctx link_config_ctx;
 typedef struct link_config link_config;

commit 493657337ad8569e0998a3afa7d6fb357757364a
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Nov 6 14:09:51 2014 +0100

    core: get rid of condition.c and move the remaining call into util.c
    
    That way only one file with condition code remaining, in src/shared/,
    rather than src/core/.
    
    Next step: dropping the "-util" suffix from condition-util.[ch].

diff --git a/Makefile.am b/Makefile.am
index c5667c3..a10c306 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1132,8 +1132,6 @@ libsystemd_core_la_SOURCES = \
 	src/core/mount-setup.h \
 	src/core/loopback-setup.h \
 	src/core/loopback-setup.c \
-	src/core/condition.c \
-	src/core/condition.h \
 	src/core/namespace.c \
 	src/core/namespace.h \
 	src/core/build.h \
diff --git a/src/core/condition.c b/src/core/condition.c
deleted file mode 100644
index c20c0f0..0000000
--- a/src/core/condition.c
+++ /dev/null
@@ -1,67 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
-  This file is part of systemd.
-
-  Copyright 2010 Lennart Poettering
-
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU Lesser General Public License as published by
-  the Free Software Foundation; either version 2.1 of the License, or
-  (at your option) any later version.
-
-  systemd is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "condition.h"
-#include "unit.h"
-
-bool condition_test_list(const char *unit, Condition *first, const char *(*to_string)(ConditionType t)) {
-        Condition *c;
-        int triggered = -1;
-
-        /* If the condition list is empty, then it is true */
-        if (!first)
-                return true;
-
-        /* Otherwise, if all of the non-trigger conditions apply and
-         * if any of the trigger conditions apply (unless there are
-         * none) we return true */
-        LIST_FOREACH(conditions, c, first) {
-                int r;
-
-                r = condition_test(c);
-                if (r < 0)
-                        log_warning_unit(unit,
-                                         "Couldn't determine result for %s=%s%s%s for %s, assuming failed: %s",
-                                         to_string(c->type),
-                                         c->trigger ? "|" : "",
-                                         c->negate ? "!" : "",
-                                         c->parameter,
-                                         unit,
-                                         strerror(-r));
-                else
-                        log_debug_unit(unit,
-                                       "%s=%s%s%s %s for %s.",
-                                       to_string(c->type),
-                                       c->trigger ? "|" : "",
-                                       c->negate ? "!" : "",
-                                       c->parameter,
-                                       condition_result_to_string(c->result),
-                                       unit);
-
-                if (!c->trigger && r <= 0)
-                        return false;
-
-                if (c->trigger && triggered <= 0)
-                        triggered = r > 0;
-        }
-
-        return triggered != 0;
-}
diff --git a/src/core/condition.h b/src/core/condition.h
deleted file mode 100644
index a6a31ed..0000000
--- a/src/core/condition.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
-  This file is part of systemd.
-
-  Copyright 2010 Lennart Poettering
-
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU Lesser General Public License as published by
-  the Free Software Foundation; either version 2.1 of the License, or
-  (at your option) any later version.
-
-  systemd is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "condition-util.h"
-
-bool condition_test_list(const char *unit, Condition *c, const char *(*to_string)(ConditionType t));
diff --git a/src/core/unit.c b/src/core/unit.c
index 66f53dd..948f8e2 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -1245,11 +1245,58 @@ fail:
         return r;
 }
 
+static bool unit_condition_test_list(Unit *u, Condition *first, const char *(*to_string)(ConditionType t)) {
+        Condition *c;
+        int triggered = -1;
+
+        assert(u);
+        assert(to_string);
+
+        /* If the condition list is empty, then it is true */
+        if (!first)
+                return true;
+
+        /* Otherwise, if all of the non-trigger conditions apply and
+         * if any of the trigger conditions apply (unless there are
+         * none) we return true */
+        LIST_FOREACH(conditions, c, first) {
+                int r;
+
+                r = condition_test(c);
+                if (r < 0)
+                        log_warning_unit(u->id,
+                                         "Couldn't determine result for %s=%s%s%s for %s, assuming failed: %s",
+                                         to_string(c->type),
+                                         c->trigger ? "|" : "",
+                                         c->negate ? "!" : "",
+                                         c->parameter,
+                                         u->id,
+                                         strerror(-r));
+                else
+                        log_debug_unit(u->id,
+                                       "%s=%s%s%s %s for %s.",
+                                       to_string(c->type),
+                                       c->trigger ? "|" : "",
+                                       c->negate ? "!" : "",
+                                       c->parameter,
+                                       condition_result_to_string(c->result),
+                                       u->id);
+
+                if (!c->trigger && r <= 0)
+                        return false;
+
+                if (c->trigger && triggered <= 0)
+                        triggered = r > 0;
+        }
+
+        return triggered != 0;
+}
+
 static bool unit_condition_test(Unit *u) {
         assert(u);
 
         dual_timestamp_get(&u->condition_timestamp);
-        u->condition_result = condition_test_list(u->id, u->conditions, condition_type_to_string);
+        u->condition_result = unit_condition_test_list(u, u->conditions, condition_type_to_string);
 
         return u->condition_result;
 }
@@ -1258,7 +1305,7 @@ static bool unit_assert_test(Unit *u) {
         assert(u);
 
         dual_timestamp_get(&u->assert_timestamp);
-        u->assert_result = condition_test_list(u->id, u->asserts, assert_type_to_string);
+        u->assert_result = unit_condition_test_list(u, u->asserts, assert_type_to_string);
 
         return u->assert_result;
 }
diff --git a/src/core/unit.h b/src/core/unit.h
index 8b24272..b5a224b 100644
--- a/src/core/unit.h
+++ b/src/core/unit.h
@@ -38,7 +38,7 @@ typedef struct UnitStatusMessageFormats UnitStatusMessageFormats;
 #include "socket-util.h"
 #include "execute.h"
 #include "cgroup.h"
-#include "condition.h"
+#include "condition-util.h"
 #include "install.h"
 #include "unit-name.h"
 #include "failure-action.h"
diff --git a/src/libsystemd-network/network-internal.c b/src/libsystemd-network/network-internal.c
index 6f16050..f3141f7 100644
--- a/src/libsystemd-network/network-internal.c
+++ b/src/libsystemd-network/network-internal.c
@@ -32,7 +32,7 @@
 #include "utf8.h"
 #include "util.h"
 #include "conf-parser.h"
-#include "condition.h"
+#include "condition-util.h"
 #include "network-internal.h"
 
 const char *net_get_name(struct udev_device *device) {
diff --git a/src/test/test-tables.c b/src/test/test-tables.c
index 97d5609..d0a621c 100644
--- a/src/test/test-tables.c
+++ b/src/test/test-tables.c
@@ -20,7 +20,7 @@
 #include "automount.h"
 #include "cgroup.h"
 #include "compress.h"
-#include "condition.h"
+#include "condition-util.h"
 #include "device.h"
 #include "execute.h"
 #include "exit-status.h"

commit c073a0c4a5ffbf6677dd6af02e7c7d59b2b901ab
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Nov 6 14:00:05 2014 +0100

    man: document that we don't document .include on purpose
    
    <!-- xml comments are useful! -->

diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml
index dddcf09..2e298ca 100644
--- a/man/systemd.unit.xml
+++ b/man/systemd.unit.xml
@@ -211,6 +211,10 @@
                 template <literal>.d/</literal> subdirectory and reads
                 its <literal>.conf</literal> files.</para>
 
+                <!-- Note that we do not document .include here, as we
+                     consider it mostly obsolete, and want people to
+                     use .d/ drop-ins instead. -->
+
                 <para>Note that while systemd offers a flexible
                 dependency system between units it is recommended to
                 use this functionality only sparingly and instead rely

commit 90a2ec10f2d43a8530aae856013518eb567c4039
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Nov 6 13:59:41 2014 +0100

    man: don't document ConditionNull=/AssertNull= as the are not particularly useful and simply confusing

diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml
index 5d6f6eb..dddcf09 100644
--- a/man/systemd.unit.xml
+++ b/man/systemd.unit.xml
@@ -950,7 +950,11 @@
                                 <term><varname>ConditionDirectoryNotEmpty=</varname></term>
                                 <term><varname>ConditionFileNotEmpty=</varname></term>
                                 <term><varname>ConditionFileIsExecutable=</varname></term>
-                                <term><varname>ConditionNull=</varname></term>
+
+                                <!-- We don't document ConditionNull=
+                                     here as it is not particularly
+                                     useful and probably just
+                                     confusing. -->
 
                                 <listitem><para>Before starting a unit
                                 verify that the specified condition is
@@ -1230,15 +1234,6 @@
                                 exists, is a regular file and marked
                                 executable.</para>
 
-                                <para>Finally,
-                                <varname>ConditionNull=</varname> may
-                                be used to add a constant condition
-                                check value to the unit. It takes a
-                                boolean argument. If set to
-                                <varname>false</varname>, the condition
-                                will always fail, otherwise
-                                succeed.</para>
-
                                 <para>If multiple conditions are
                                 specified, the unit will be executed if
                                 all of them apply (i.e. a logical AND
@@ -1283,7 +1278,6 @@
                                 <term><varname>AssertDirectoryNotEmpty=</varname></term>
                                 <term><varname>AssertFileNotEmpty=</varname></term>
                                 <term><varname>AssertFileIsExecutable=</varname></term>
-                                <term><varname>AssertNull=</varname></term>
 
                                 <listitem><para>Similar to the
                                 <varname>ConditionArchitecture=</varname>,

commit 651c33185eeb449385b471fffa8882a163c496f0
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Nov 6 13:56:22 2014 +0100

    condition: order condition types the same way in man page, enum, tables
    
    Yes, sometimes I develop OCD.

diff --git a/src/shared/condition-util.c b/src/shared/condition-util.c
index ee9d11e..5db20a7 100644
--- a/src/shared/condition-util.c
+++ b/src/shared/condition-util.c
@@ -472,6 +472,15 @@ void condition_dump_list(Condition *first, FILE *f, const char *prefix, const ch
 }
 
 static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
+        [CONDITION_ARCHITECTURE] = "ConditionArchitecture",
+        [CONDITION_VIRTUALIZATION] = "ConditionVirtualization",
+        [CONDITION_HOST] = "ConditionHost",
+        [CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine",
+        [CONDITION_SECURITY] = "ConditionSecurity",
+        [CONDITION_CAPABILITY] = "ConditionCapability",
+        [CONDITION_AC_POWER] = "ConditionACPower",
+        [CONDITION_NEEDS_UPDATE] = "ConditionNeedsUpdate",
+        [CONDITION_FIRST_BOOT] = "ConditionFirstBoot",
         [CONDITION_PATH_EXISTS] = "ConditionPathExists",
         [CONDITION_PATH_EXISTS_GLOB] = "ConditionPathExistsGlob",
         [CONDITION_PATH_IS_DIRECTORY] = "ConditionPathIsDirectory",
@@ -481,21 +490,21 @@ static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
         [CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty",
         [CONDITION_FILE_NOT_EMPTY] = "ConditionFileNotEmpty",
         [CONDITION_FILE_IS_EXECUTABLE] = "ConditionFileIsExecutable",
-        [CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine",
-        [CONDITION_VIRTUALIZATION] = "ConditionVirtualization",
-        [CONDITION_SECURITY] = "ConditionSecurity",
-        [CONDITION_CAPABILITY] = "ConditionCapability",
-        [CONDITION_HOST] = "ConditionHost",
-        [CONDITION_AC_POWER] = "ConditionACPower",
-        [CONDITION_ARCHITECTURE] = "ConditionArchitecture",
-        [CONDITION_NEEDS_UPDATE] = "ConditionNeedsUpdate",
-        [CONDITION_FIRST_BOOT] = "ConditionFirstBoot",
         [CONDITION_NULL] = "ConditionNull"
 };
 
 DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType);
 
 static const char* const assert_type_table[_CONDITION_TYPE_MAX] = {
+        [CONDITION_ARCHITECTURE] = "AssertArchitecture",
+        [CONDITION_VIRTUALIZATION] = "AssertVirtualization",
+        [CONDITION_HOST] = "AssertHost",
+        [CONDITION_KERNEL_COMMAND_LINE] = "AssertKernelCommandLine",
+        [CONDITION_SECURITY] = "AssertSecurity",
+        [CONDITION_CAPABILITY] = "AssertCapability",
+        [CONDITION_AC_POWER] = "AssertACPower",
+        [CONDITION_NEEDS_UPDATE] = "AssertNeedsUpdate",
+        [CONDITION_FIRST_BOOT] = "AssertFirstBoot",
         [CONDITION_PATH_EXISTS] = "AssertPathExists",
         [CONDITION_PATH_EXISTS_GLOB] = "AssertPathExistsGlob",
         [CONDITION_PATH_IS_DIRECTORY] = "AssertPathIsDirectory",
@@ -505,15 +514,6 @@ static const char* const assert_type_table[_CONDITION_TYPE_MAX] = {
         [CONDITION_DIRECTORY_NOT_EMPTY] = "AssertDirectoryNotEmpty",
         [CONDITION_FILE_NOT_EMPTY] = "AssertFileNotEmpty",
         [CONDITION_FILE_IS_EXECUTABLE] = "AssertFileIsExecutable",
-        [CONDITION_KERNEL_COMMAND_LINE] = "AssertKernelCommandLine",
-        [CONDITION_VIRTUALIZATION] = "AssertVirtualization",
-        [CONDITION_SECURITY] = "AssertSecurity",
-        [CONDITION_CAPABILITY] = "AssertCapability",
-        [CONDITION_HOST] = "AssertHost",
-        [CONDITION_AC_POWER] = "AssertACPower",
-        [CONDITION_ARCHITECTURE] = "AssertArchitecture",
-        [CONDITION_NEEDS_UPDATE] = "AssertNeedsUpdate",
-        [CONDITION_FIRST_BOOT] = "AssertFirstBoot",
         [CONDITION_NULL] = "AssertNull"
 };
 
diff --git a/src/shared/condition-util.h b/src/shared/condition-util.h
index 9aaaee9..28d1d94 100644
--- a/src/shared/condition-util.h
+++ b/src/shared/condition-util.h
@@ -28,7 +28,16 @@
 #include "macro.h"
 
 typedef enum ConditionType {
-        CONDITION_NULL,
+        CONDITION_ARCHITECTURE,
+        CONDITION_VIRTUALIZATION,
+        CONDITION_HOST,
+        CONDITION_KERNEL_COMMAND_LINE,
+        CONDITION_SECURITY,
+        CONDITION_CAPABILITY,
+        CONDITION_AC_POWER,
+
+        CONDITION_NEEDS_UPDATE,
+        CONDITION_FIRST_BOOT,
 
         CONDITION_PATH_EXISTS,
         CONDITION_PATH_EXISTS_GLOB,
@@ -40,16 +49,7 @@ typedef enum ConditionType {
         CONDITION_FILE_NOT_EMPTY,
         CONDITION_FILE_IS_EXECUTABLE,
 
-        CONDITION_KERNEL_COMMAND_LINE,
-        CONDITION_VIRTUALIZATION,
-        CONDITION_ARCHITECTURE,
-        CONDITION_SECURITY,
-        CONDITION_CAPABILITY,
-        CONDITION_HOST,
-        CONDITION_AC_POWER,
-
-        CONDITION_NEEDS_UPDATE,
-        CONDITION_FIRST_BOOT,
+        CONDITION_NULL,
 
         _CONDITION_TYPE_MAX,
         _CONDITION_TYPE_INVALID = -1

commit 59fccdc587bc179c1638916ee16a24099f94f81f
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Nov 6 13:43:45 2014 +0100

    core: introduce the concept of AssertXYZ= similar to ConditionXYZ=, but fatal for a start job if not met

diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml
index 6d4c5c1..5d6f6eb 100644
--- a/man/systemd.unit.xml
+++ b/man/systemd.unit.xml
@@ -1265,6 +1265,39 @@
                         </varlistentry>
 
                         <varlistentry>
+                                <term><varname>AssertArchitecture=</varname></term>
+                                <term><varname>AssertVirtualization=</varname></term>
+                                <term><varname>AssertHost=</varname></term>
+                                <term><varname>AssertKernelCommandLine=</varname></term>
+                                <term><varname>AssertSecurity=</varname></term>
+                                <term><varname>AssertCapability=</varname></term>
+                                <term><varname>AssertACPower=</varname></term>
+                                <term><varname>AssertNeedsUpdate=</varname></term>
+                                <term><varname>AssertFirstBoot=</varname></term>
+                                <term><varname>AssertPathExists=</varname></term>
+                                <term><varname>AssertPathExistsGlob=</varname></term>
+                                <term><varname>AssertPathIsDirectory=</varname></term>
+                                <term><varname>AssertPathIsSymbolicLink=</varname></term>
+                                <term><varname>AssertPathIsMountPoint=</varname></term>
+                                <term><varname>AssertPathIsReadWrite=</varname></term>
+                                <term><varname>AssertDirectoryNotEmpty=</varname></term>
+                                <term><varname>AssertFileNotEmpty=</varname></term>
+                                <term><varname>AssertFileIsExecutable=</varname></term>
+                                <term><varname>AssertNull=</varname></term>
+
+                                <listitem><para>Similar to the
+                                <varname>ConditionArchitecture=</varname>,
+                                <varname>ConditionVirtualization=</varname>,
+                                ... condition settings described above
+                                these settings add assertion checks to
+                                the start-up of the unit. However,
+                                unlike the conditions settings any
+                                assertion setting that is not met
+                                results in failure of the start
+                                job it was triggered by.</para></listitem>
+                        </varlistentry>
+
+                        <varlistentry>
                                 <term><varname>SourcePath=</varname></term>
                                 <listitem><para>A path to a
                                 configuration file this unit has been
diff --git a/src/core/condition.c b/src/core/condition.c
index 8475258..c20c0f0 100644
--- a/src/core/condition.c
+++ b/src/core/condition.c
@@ -22,7 +22,7 @@
 #include "condition.h"
 #include "unit.h"
 
-bool condition_test_list(const char *unit, Condition *first) {
+bool condition_test_list(const char *unit, Condition *first, const char *(*to_string)(ConditionType t)) {
         Condition *c;
         int triggered = -1;
 
@@ -40,7 +40,7 @@ bool condition_test_list(const char *unit, Condition *first) {
                 if (r < 0)
                         log_warning_unit(unit,
                                          "Couldn't determine result for %s=%s%s%s for %s, assuming failed: %s",
-                                         condition_type_to_string(c->type),
+                                         to_string(c->type),
                                          c->trigger ? "|" : "",
                                          c->negate ? "!" : "",
                                          c->parameter,
@@ -49,7 +49,7 @@ bool condition_test_list(const char *unit, Condition *first) {
                 else
                         log_debug_unit(unit,
                                        "%s=%s%s%s %s for %s.",
-                                       condition_type_to_string(c->type),
+                                       to_string(c->type),
                                        c->trigger ? "|" : "",
                                        c->negate ? "!" : "",
                                        c->parameter,
diff --git a/src/core/condition.h b/src/core/condition.h
index 6dd77bb..a6a31ed 100644
--- a/src/core/condition.h
+++ b/src/core/condition.h
@@ -23,4 +23,4 @@
 
 #include "condition-util.h"
 
-bool condition_test_list(const char *unit, Condition *c);
+bool condition_test_list(const char *unit, Condition *c, const char *(*to_string)(ConditionType t));
diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c
index 3fa4271..5dcde25 100644
--- a/src/core/dbus-unit.c
+++ b/src/core/dbus-unit.c
@@ -315,19 +315,21 @@ static int property_get_conditions(
                 void *userdata,
                 sd_bus_error *error) {
 
-        Unit *u = userdata;
-        Condition *c;
+        const char *(*to_string)(ConditionType type) = NULL;
+        Condition **list = userdata, *c;
         int r;
 
         assert(bus);
         assert(reply);
-        assert(u);
+        assert(list);
+
+        to_string = streq(property, "Asserts") ? assert_type_to_string : condition_type_to_string;
 
         r = sd_bus_message_open_container(reply, 'a', "(sbbsi)");
         if (r < 0)
                 return r;
 
-        LIST_FOREACH(conditions, c, u->conditions) {
+        LIST_FOREACH(conditions, c, *list) {
                 int tristate;
 
                 tristate =
@@ -335,7 +337,7 @@ static int property_get_conditions(
                         c->result == CONDITION_SUCCEEDED ? 1 : -1;
 
                 r = sd_bus_message_append(reply, "(sbbsi)",
-                                          condition_type_to_string(c->type),
+                                          to_string(c->type),
                                           c->trigger, c->negate,
                                           c->parameter, tristate);
                 if (r < 0)
@@ -572,8 +574,11 @@ const sd_bus_vtable bus_unit_vtable[] = {
         SD_BUS_PROPERTY("JobTimeoutAction", "s", property_get_failure_action, offsetof(Unit, job_timeout_action), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("JobTimeoutRebootArgument", "s", NULL, offsetof(Unit, job_timeout_reboot_arg), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("ConditionResult", "b", bus_property_get_bool, offsetof(Unit, condition_result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+        SD_BUS_PROPERTY("AssertResult", "b", bus_property_get_bool, offsetof(Unit, assert_result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
         BUS_PROPERTY_DUAL_TIMESTAMP("ConditionTimestamp", offsetof(Unit, condition_timestamp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
-        SD_BUS_PROPERTY("Conditions", "a(sbbsi)", property_get_conditions, 0, 0),
+        BUS_PROPERTY_DUAL_TIMESTAMP("AssertTimestamp", offsetof(Unit, assert_timestamp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+        SD_BUS_PROPERTY("Conditions", "a(sbbsi)", property_get_conditions, offsetof(Unit, conditions), 0),
+        SD_BUS_PROPERTY("Asserts", "a(sbbsi)", property_get_conditions, offsetof(Unit, asserts), 0),
         SD_BUS_PROPERTY("LoadError", "(ss)", property_get_load_error, 0, SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("Transient", "b", bus_property_get_bool, offsetof(Unit, transient), SD_BUS_VTABLE_PROPERTY_CONST),
 
diff --git a/src/core/job.c b/src/core/job.c
index eaa4bb1..51d1581 100644
--- a/src/core/job.c
+++ b/src/core/job.c
@@ -542,6 +542,8 @@ int job_run_and_invalidate(Job *j) {
                         r = job_finish_and_invalidate(j, JOB_SKIPPED, true);
                 else if (r == -ENOEXEC)
                         r = job_finish_and_invalidate(j, JOB_INVALID, true);
+                else if (r == -EPROTO)
+                        r = job_finish_and_invalidate(j, JOB_ASSERT, true);
                 else if (r == -EAGAIN) {
                         j->state = JOB_WAITING;
                         m->n_running_jobs--;
@@ -655,6 +657,11 @@ static void job_print_status_message(Unit *u, JobType t, JobResult result) {
                         unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " TIME " ANSI_HIGHLIGHT_OFF, format);
                         break;
 
+                case JOB_ASSERT:
+                        manager_flip_auto_status(u->manager, true);
+                        unit_status_printf(u, ANSI_HIGHLIGHT_YELLOW_ON "ASSERT" ANSI_HIGHLIGHT_OFF, format);
+                        break;
+
                 default:
                         ;
                 }
@@ -1189,6 +1196,7 @@ static const char* const job_result_table[_JOB_RESULT_MAX] = {
         [JOB_DEPENDENCY] = "dependency",
         [JOB_SKIPPED] = "skipped",
         [JOB_INVALID] = "invalid",
+        [JOB_ASSERT] = "assert",
 };
 
 DEFINE_STRING_TABLE_LOOKUP(job_result, JobResult);
diff --git a/src/core/job.h b/src/core/job.h
index 1e7c61b..b7ebd8d 100644
--- a/src/core/job.h
+++ b/src/core/job.h
@@ -99,6 +99,7 @@ enum JobResult {
         JOB_DEPENDENCY,          /* A required dependency job did not result in JOB_DONE */
         JOB_SKIPPED,             /* Negative result of JOB_VERIFY_ACTIVE */
         JOB_INVALID,             /* JOB_RELOAD of inactive unit */
+        JOB_ASSERT,              /* Couldn't start a unit, because an assert didn't hold */
         _JOB_RESULT_MAX,
         _JOB_RESULT_INVALID = -1
 };
diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4
index 5158a9f..1d2debe 100644
--- a/src/core/load-fragment-gperf.gperf.m4
+++ b/src/core/load-fragment-gperf.gperf.m4
@@ -155,25 +155,44 @@ Unit.IgnoreOnSnapshot,           config_parse_bool,                  0,
 Unit.JobTimeoutSec,              config_parse_sec,                   0,                             offsetof(Unit, job_timeout)
 Unit.JobTimeoutAction,           config_parse_failure_action,        0,                             offsetof(Unit, job_timeout_action)
 Unit.JobTimeoutRebootArgument,   config_parse_string,                0,                             offsetof(Unit, job_timeout_reboot_arg)
-Unit.ConditionPathExists,        config_parse_unit_condition_path,   CONDITION_PATH_EXISTS,         0
-Unit.ConditionPathExistsGlob,    config_parse_unit_condition_path,   CONDITION_PATH_EXISTS_GLOB,    0
-Unit.ConditionPathIsDirectory,   config_parse_unit_condition_path,   CONDITION_PATH_IS_DIRECTORY,   0
-Unit.ConditionPathIsSymbolicLink,config_parse_unit_condition_path,   CONDITION_PATH_IS_SYMBOLIC_LINK,0
-Unit.ConditionPathIsMountPoint,  config_parse_unit_condition_path,   CONDITION_PATH_IS_MOUNT_POINT, 0
-Unit.ConditionPathIsReadWrite,   config_parse_unit_condition_path,   CONDITION_PATH_IS_READ_WRITE,  0
-Unit.ConditionDirectoryNotEmpty, config_parse_unit_condition_path,   CONDITION_DIRECTORY_NOT_EMPTY, 0
-Unit.ConditionFileNotEmpty,      config_parse_unit_condition_path,   CONDITION_FILE_NOT_EMPTY,      0
-Unit.ConditionFileIsExecutable,  config_parse_unit_condition_path,   CONDITION_FILE_IS_EXECUTABLE,  0
-Unit.ConditionNeedsUpdate,       config_parse_unit_condition_path,   CONDITION_NEEDS_UPDATE,        0
-Unit.ConditionFirstBoot,         config_parse_unit_condition_string, CONDITION_FIRST_BOOT,          0
-Unit.ConditionKernelCommandLine, config_parse_unit_condition_string, CONDITION_KERNEL_COMMAND_LINE, 0
-Unit.ConditionArchitecture,      config_parse_unit_condition_string, CONDITION_ARCHITECTURE,        0
-Unit.ConditionVirtualization,    config_parse_unit_condition_string, CONDITION_VIRTUALIZATION,      0
-Unit.ConditionSecurity,          config_parse_unit_condition_string, CONDITION_SECURITY,            0
-Unit.ConditionCapability,        config_parse_unit_condition_string, CONDITION_CAPABILITY,          0
-Unit.ConditionHost,              config_parse_unit_condition_string, CONDITION_HOST,                0
-Unit.ConditionACPower,           config_parse_unit_condition_string, CONDITION_AC_POWER,            0
-Unit.ConditionNull,              config_parse_unit_condition_null,   0,                             0
+Unit.ConditionPathExists,        config_parse_unit_condition_path,   CONDITION_PATH_EXISTS,         offsetof(Unit, conditions)
+Unit.ConditionPathExistsGlob,    config_parse_unit_condition_path,   CONDITION_PATH_EXISTS_GLOB,    offsetof(Unit, conditions)
+Unit.ConditionPathIsDirectory,   config_parse_unit_condition_path,   CONDITION_PATH_IS_DIRECTORY,   offsetof(Unit, conditions)
+Unit.ConditionPathIsSymbolicLink,config_parse_unit_condition_path,   CONDITION_PATH_IS_SYMBOLIC_LINK,offsetof(Unit, conditions)
+Unit.ConditionPathIsMountPoint,  config_parse_unit_condition_path,   CONDITION_PATH_IS_MOUNT_POINT, offsetof(Unit, conditions)
+Unit.ConditionPathIsReadWrite,   config_parse_unit_condition_path,   CONDITION_PATH_IS_READ_WRITE,  offsetof(Unit, conditions)
+Unit.ConditionDirectoryNotEmpty, config_parse_unit_condition_path,   CONDITION_DIRECTORY_NOT_EMPTY, offsetof(Unit, conditions)
+Unit.ConditionFileNotEmpty,      config_parse_unit_condition_path,   CONDITION_FILE_NOT_EMPTY,      offsetof(Unit, conditions)
+Unit.ConditionFileIsExecutable,  config_parse_unit_condition_path,   CONDITION_FILE_IS_EXECUTABLE,  offsetof(Unit, conditions)
+Unit.ConditionNeedsUpdate,       config_parse_unit_condition_path,   CONDITION_NEEDS_UPDATE,        offsetof(Unit, conditions)
+Unit.ConditionFirstBoot,         config_parse_unit_condition_string, CONDITION_FIRST_BOOT,          offsetof(Unit, conditions)
+Unit.ConditionKernelCommandLine, config_parse_unit_condition_string, CONDITION_KERNEL_COMMAND_LINE, offsetof(Unit, conditions)
+Unit.ConditionArchitecture,      config_parse_unit_condition_string, CONDITION_ARCHITECTURE,        offsetof(Unit, conditions)
+Unit.ConditionVirtualization,    config_parse_unit_condition_string, CONDITION_VIRTUALIZATION,      offsetof(Unit, conditions)
+Unit.ConditionSecurity,          config_parse_unit_condition_string, CONDITION_SECURITY,            offsetof(Unit, conditions)
+Unit.ConditionCapability,        config_parse_unit_condition_string, CONDITION_CAPABILITY,          offsetof(Unit, conditions)
+Unit.ConditionHost,              config_parse_unit_condition_string, CONDITION_HOST,                offsetof(Unit, conditions)
+Unit.ConditionACPower,           config_parse_unit_condition_string, CONDITION_AC_POWER,            offsetof(Unit, conditions)
+Unit.ConditionNull,              config_parse_unit_condition_null,   0,                             offsetof(Unit, conditions)
+Unit.AssertPathExists,           config_parse_unit_condition_path,   CONDITION_PATH_EXISTS,         offsetof(Unit, asserts)
+Unit.AssertPathExistsGlob,       config_parse_unit_condition_path,   CONDITION_PATH_EXISTS_GLOB,    offsetof(Unit, asserts)
+Unit.AssertPathIsDirectory,      config_parse_unit_condition_path,   CONDITION_PATH_IS_DIRECTORY,   offsetof(Unit, asserts)
+Unit.AssertPathIsSymbolicLink,   config_parse_unit_condition_path,   CONDITION_PATH_IS_SYMBOLIC_LINK,offsetof(Unit, asserts)
+Unit.AssertPathIsMountPoint,     config_parse_unit_condition_path,   CONDITION_PATH_IS_MOUNT_POINT, offsetof(Unit, asserts)
+Unit.AssertPathIsReadWrite,      config_parse_unit_condition_path,   CONDITION_PATH_IS_READ_WRITE,  offsetof(Unit, asserts)
+Unit.AssertDirectoryNotEmpty,    config_parse_unit_condition_path,   CONDITION_DIRECTORY_NOT_EMPTY, offsetof(Unit, asserts)
+Unit.AssertFileNotEmpty,         config_parse_unit_condition_path,   CONDITION_FILE_NOT_EMPTY,      offsetof(Unit, asserts)
+Unit.AssertFileIsExecutable,     config_parse_unit_condition_path,   CONDITION_FILE_IS_EXECUTABLE,  offsetof(Unit, asserts)
+Unit.AssertNeedsUpdate,          config_parse_unit_condition_path,   CONDITION_NEEDS_UPDATE,        offsetof(Unit, asserts)
+Unit.AssertFirstBoot,            config_parse_unit_condition_string, CONDITION_FIRST_BOOT,          offsetof(Unit, asserts)
+Unit.AssertKernelCommandLine,    config_parse_unit_condition_string, CONDITION_KERNEL_COMMAND_LINE, offsetof(Unit, asserts)
+Unit.AssertArchitecture,         config_parse_unit_condition_string, CONDITION_ARCHITECTURE,        offsetof(Unit, asserts)
+Unit.AssertVirtualization,       config_parse_unit_condition_string, CONDITION_VIRTUALIZATION,      offsetof(Unit, asserts)
+Unit.AssertSecurity,             config_parse_unit_condition_string, CONDITION_SECURITY,            offsetof(Unit, asserts)
+Unit.AssertCapability,           config_parse_unit_condition_string, CONDITION_CAPABILITY,          offsetof(Unit, asserts)
+Unit.AssertHost,                 config_parse_unit_condition_string, CONDITION_HOST,                offsetof(Unit, asserts)
+Unit.AssertACPower,              config_parse_unit_condition_string, CONDITION_AC_POWER,            offsetof(Unit, asserts)
+Unit.AssertNull,                 config_parse_unit_condition_null,   0,                             offsetof(Unit, asserts)
 m4_dnl
 Service.PIDFile,                 config_parse_unit_path_printf,      0,                             offsetof(Service, pid_file)
 Service.ExecStartPre,            config_parse_exec,                  SERVICE_EXEC_START_PRE,        offsetof(Service, exec_command)
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index e193a67..2ee16bd 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -1955,22 +1955,23 @@ int config_parse_ip_tos(const char *unit,
         return 0;
 }
 
-int config_parse_unit_condition_path(const char *unit,
-                                     const char *filename,
-                                     unsigned line,
-                                     const char *section,
-                                     unsigned section_line,
-                                     const char *lvalue,
-                                     int ltype,
-                                     const char *rvalue,
-                                     void *data,
-                                     void *userdata) {
+int config_parse_unit_condition_path(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
 
-        ConditionType cond = ltype;
-        Unit *u = data;
-        bool trigger, negate;
-        Condition *c;
         _cleanup_free_ char *p = NULL;
+        Condition **list = data, *c;
+        ConditionType t = ltype;
+        bool trigger, negate;
+        Unit *u = userdata;
         int r;
 
         assert(filename);
@@ -1980,8 +1981,8 @@ 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;
+                condition_free_list(*list);
+                *list = NULL;
                 return 0;
         }
 
@@ -1994,45 +1995,41 @@ int config_parse_unit_condition_path(const char *unit,
                 rvalue++;
 
         r = unit_full_printf(u, rvalue, &p);
-        if (r < 0)
-                log_syntax(unit, LOG_ERR, filename, line, -r,
-                           "Failed to resolve specifiers, ignoring: %s", rvalue);
-        if (!p) {
-                p = strdup(rvalue);
-                if (!p)
-                        return log_oom();
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve specifiers, ignoring: %s", rvalue);
+                return 0;
         }
 
         if (!path_is_absolute(p)) {
-                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                           "Path in condition not absolute, ignoring: %s", p);
+                log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Path in condition not absolute, ignoring: %s", p);
                 return 0;
         }
 
-        c = condition_new(cond, p, trigger, negate);
+        c = condition_new(t, p, trigger, negate);
         if (!c)
                 return log_oom();
 
-        LIST_PREPEND(conditions, u->conditions, c);
+        LIST_PREPEND(conditions, *list, c);
         return 0;
 }
 
-int config_parse_unit_condition_string(const char *unit,
-                                       const char *filename,
-                                       unsigned line,
-                                       const char *section,
-                                       unsigned section_line,
-                                       const char *lvalue,
-                                       int ltype,
-                                       const char *rvalue,
-                                       void *data,
-                                       void *userdata) {
+int config_parse_unit_condition_string(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
 
-        ConditionType cond = ltype;
-        Unit *u = data;
-        bool trigger, negate;
-        Condition *c;
         _cleanup_free_ char *s = NULL;
+        Condition **list = data, *c;
+        ConditionType t = ltype;
+        bool trigger, negate;
+        Unit *u = userdata;
         int r;
 
         assert(filename);
@@ -2042,8 +2039,8 @@ 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;
+                condition_free_list(*list);
+                *list = NULL;
                 return 0;
         }
 
@@ -2056,36 +2053,32 @@ int config_parse_unit_condition_string(const char *unit,
                 rvalue++;
 
         r = unit_full_printf(u, rvalue, &s);
-        if (r < 0)
-                log_syntax(unit, LOG_ERR, filename, line, -r,
-                           "Failed to resolve specifiers, ignoring: %s", rvalue);
-        if (!s) {
-                s = strdup(rvalue);
-                if (!s)
-                        return log_oom();
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve specifiers, ignoring: %s", rvalue);
+                return 0;
         }
 
-        c = condition_new(cond, s, trigger, negate);
+        c = condition_new(t, s, trigger, negate);
         if (!c)
                 return log_oom();
 
-        LIST_PREPEND(conditions, u->conditions, c);
+        LIST_PREPEND(conditions, *list, c);
         return 0;
 }
 
-int config_parse_unit_condition_null(const char *unit,
-                                     const char *filename,
-                                     unsigned line,
-                                     const char *section,
-                                     unsigned section_line,
-                                     const char *lvalue,
-                                     int ltype,
-                                     const char *rvalue,
-                                     void *data,
-                                     void *userdata) {
+int config_parse_unit_condition_null(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
 
-        Unit *u = data;
-        Condition *c;
+        Condition **list = data, *c;
         bool trigger, negate;
         int b;
 
@@ -2096,8 +2089,8 @@ 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;
+                condition_free_list(*list);
+                *list = NULL;
                 return 0;
         }
 
@@ -2111,9 +2104,7 @@ int config_parse_unit_condition_null(const char *unit,
 
         b = parse_boolean(rvalue);
         if (b < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, -b,
-                           "Failed to parse boolean value in condition, ignoring: %s",
-                           rvalue);
+                log_syntax(unit, LOG_ERR, filename, line, -b, "Failed to parse boolean value in condition, ignoring: %s", rvalue);
                 return 0;
         }
 
@@ -2124,7 +2115,7 @@ int config_parse_unit_condition_null(const char *unit,
         if (!c)
                 return log_oom();
 
-        LIST_PREPEND(conditions, u->conditions, c);
+        LIST_PREPEND(conditions, *list, c);
         return 0;
 }
 
diff --git a/src/core/unit.c b/src/core/unit.c
index d5acc72..66f53dd 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -527,6 +527,7 @@ void unit_free(Unit *u) {
         unit_unwatch_all_pids(u);
 
         condition_free_list(u->conditions);
+        condition_free_list(u->asserts);
 
         unit_ref_unset(&u->slice);
 
@@ -929,7 +930,8 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
         if (u->job_timeout_reboot_arg)
                 fprintf(f, "%s\tJob Timeout Reboot Argument: %s\n", prefix, u->job_timeout_reboot_arg);
 
-        condition_dump_list(u->conditions, f, prefix);
+        condition_dump_list(u->conditions, f, prefix, condition_type_to_string);
+        condition_dump_list(u->asserts, f, prefix, assert_type_to_string);
 
         if (dual_timestamp_is_set(&u->condition_timestamp))
                 fprintf(f,
@@ -938,6 +940,13 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
                         prefix, strna(format_timestamp(timestamp1, sizeof(timestamp1), u->condition_timestamp.realtime)),
                         prefix, yes_no(u->condition_result));
 
+        if (dual_timestamp_is_set(&u->assert_timestamp))
+                fprintf(f,
+                        "%s\tAssert Timestamp: %s\n"
+                        "%s\tAssert Result: %s\n",
+                        prefix, strna(format_timestamp(timestamp1, sizeof(timestamp1), u->assert_timestamp.realtime)),
+                        prefix, yes_no(u->assert_result));
+
         for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++) {
                 Unit *other;
 
@@ -1240,11 +1249,20 @@ static 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, condition_type_to_string);
 
         return u->condition_result;
 }
 
+static bool unit_assert_test(Unit *u) {
+        assert(u);
+
+        dual_timestamp_get(&u->assert_timestamp);
+        u->assert_result = condition_test_list(u->id, u->asserts, assert_type_to_string);
+
+        return u->assert_result;
+}
+
 _pure_ static const char* unit_get_status_message_format(Unit *u, JobType t) {
         const UnitStatusMessageFormats *format_table;
 
@@ -1341,6 +1359,7 @@ static void unit_status_log_starting_stopping_reloading(Unit *u, JobType t) {
  *         -EALREADY:  Unit is already started.
  *         -EAGAIN:    An operation is already in progress. Retry later.
  *         -ECANCELED: Too many requests for now.
+ *         -EPROTO:    Assert failed
  */
 int unit_start(Unit *u) {
         UnitActiveState state;
@@ -1365,15 +1384,21 @@ int unit_start(Unit *u) {
          * but we don't want to recheck the condition in that case. */
         if (state != UNIT_ACTIVATING &&
             !unit_condition_test(u)) {
-                log_debug_unit(u->id, "Starting of %s requested but condition failed. Ignoring.", u->id);
+                log_debug_unit(u->id, "Starting of %s requested but condition failed. Not starting unit.", u->id);
                 return -EALREADY;
         }
 
+        /* If the asserts failed, fail the entire job */
+        if (state != UNIT_ACTIVATING &&
+            !unit_assert_test(u)) {
+                log_debug_unit(u->id, "Starting of %s requested but asserts failed.", u->id);
+                return -EPROTO;
+        }
+
         /* Forward to the main object, if we aren't it. */
         following = unit_following(u);
         if (following) {
-                log_debug_unit(u->id, "Redirecting start request from %s to %s.",
-                               u->id, following->id);
+                log_debug_unit(u->id, "Redirecting start request from %s to %s.", u->id, following->id);
                 return unit_start(following);
         }
 
@@ -2502,10 +2527,14 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) {
         dual_timestamp_serialize(f, "active-exit-timestamp", &u->active_exit_timestamp);
         dual_timestamp_serialize(f, "inactive-enter-timestamp", &u->inactive_enter_timestamp);
         dual_timestamp_serialize(f, "condition-timestamp", &u->condition_timestamp);
+        dual_timestamp_serialize(f, "assert-timestamp", &u->assert_timestamp);
 
         if (dual_timestamp_is_set(&u->condition_timestamp))
                 unit_serialize_item(u, f, "condition-result", yes_no(u->condition_result));
 
+        if (dual_timestamp_is_set(&u->assert_timestamp))
+                unit_serialize_item(u, f, "assert-result", yes_no(u->assert_result));
+
         unit_serialize_item(u, f, "transient", yes_no(u->transient));
 
         if (u->cgroup_path)
@@ -2645,6 +2674,9 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
                 } else if (streq(l, "condition-timestamp")) {
                         dual_timestamp_deserialize(v, &u->condition_timestamp);
                         continue;
+                } else if (streq(l, "assert-timestamp")) {
+                        dual_timestamp_deserialize(v, &u->assert_timestamp);
+                        continue;
                 } else if (streq(l, "condition-result")) {
                         int b;
 
@@ -2656,6 +2688,17 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
 
                         continue;
 
+                } else if (streq(l, "assert-result")) {
+                        int b;
+
+                        b = parse_boolean(v);
+                        if (b < 0)
+                                log_debug("Failed to parse assert result value %s", v);
+                        else
+                                u->assert_result = b;
+
+                        continue;
+
                 } else if (streq(l, "transient")) {
                         int b;
 
diff --git a/src/core/unit.h b/src/core/unit.h
index 081ab18..8b24272 100644
--- a/src/core/unit.h
+++ b/src/core/unit.h
@@ -129,8 +129,10 @@ struct Unit {
 
         /* Conditions to check */
         LIST_HEAD(Condition, conditions);
+        LIST_HEAD(Condition, asserts);
 
         dual_timestamp condition_timestamp;
+        dual_timestamp assert_timestamp;
 
         dual_timestamp inactive_exit_timestamp;
         dual_timestamp active_enter_timestamp;
@@ -212,6 +214,7 @@ struct Unit {
 
         /* Did the last condition check succeed? */
         bool condition_result;
+        bool assert_result;
 
         /* Is this a transient unit? */
         bool transient;
diff --git a/src/shared/condition-util.c b/src/shared/condition-util.c
index 640a931..ee9d11e 100644
--- a/src/shared/condition-util.c
+++ b/src/shared/condition-util.c
@@ -447,7 +447,7 @@ int condition_test(Condition *c) {
         return b;
 }
 
-void condition_dump(Condition *c, FILE *f, const char *prefix) {
+void condition_dump(Condition *c, FILE *f, const char *prefix, const char *(*to_string)(ConditionType t)) {
         assert(c);
         assert(f);
 
@@ -457,18 +457,18 @@ void condition_dump(Condition *c, FILE *f, const char *prefix) {
         fprintf(f,
                 "%s\t%s: %s%s%s %s\n",
                 prefix,
-                condition_type_to_string(c->type),
+                to_string(c->type),
                 c->trigger ? "|" : "",
                 c->negate ? "!" : "",
                 c->parameter,
                 condition_result_to_string(c->result));
 }
 
-void condition_dump_list(Condition *first, FILE *f, const char *prefix) {
+void condition_dump_list(Condition *first, FILE *f, const char *prefix, const char *(*to_string)(ConditionType t)) {
         Condition *c;
 
         LIST_FOREACH(conditions, c, first)
-                condition_dump(c, f, prefix);
+                condition_dump(c, f, prefix, to_string);
 }
 
 static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
@@ -495,6 +495,30 @@ static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
 
 DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType);
 
+static const char* const assert_type_table[_CONDITION_TYPE_MAX] = {
+        [CONDITION_PATH_EXISTS] = "AssertPathExists",
+        [CONDITION_PATH_EXISTS_GLOB] = "AssertPathExistsGlob",
+        [CONDITION_PATH_IS_DIRECTORY] = "AssertPathIsDirectory",
+        [CONDITION_PATH_IS_SYMBOLIC_LINK] = "AssertPathIsSymbolicLink",
+        [CONDITION_PATH_IS_MOUNT_POINT] = "AssertPathIsMountPoint",
+        [CONDITION_PATH_IS_READ_WRITE] = "AssertPathIsReadWrite",
+        [CONDITION_DIRECTORY_NOT_EMPTY] = "AssertDirectoryNotEmpty",
+        [CONDITION_FILE_NOT_EMPTY] = "AssertFileNotEmpty",
+        [CONDITION_FILE_IS_EXECUTABLE] = "AssertFileIsExecutable",
+        [CONDITION_KERNEL_COMMAND_LINE] = "AssertKernelCommandLine",
+        [CONDITION_VIRTUALIZATION] = "AssertVirtualization",
+        [CONDITION_SECURITY] = "AssertSecurity",
+        [CONDITION_CAPABILITY] = "AssertCapability",
+        [CONDITION_HOST] = "AssertHost",
+        [CONDITION_AC_POWER] = "AssertACPower",
+        [CONDITION_ARCHITECTURE] = "AssertArchitecture",
+        [CONDITION_NEEDS_UPDATE] = "AssertNeedsUpdate",
+        [CONDITION_FIRST_BOOT] = "AssertFirstBoot",
+        [CONDITION_NULL] = "AssertNull"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(assert_type, ConditionType);
+
 static const char* const condition_result_table[_CONDITION_RESULT_MAX] = {
         [CONDITION_UNTESTED] = "untested",
         [CONDITION_SUCCEEDED] = "succeeded",
diff --git a/src/shared/condition-util.h b/src/shared/condition-util.h
index 08aee94..9aaaee9 100644
--- a/src/shared/condition-util.h
+++ b/src/shared/condition-util.h
@@ -28,6 +28,8 @@
 #include "macro.h"
 
 typedef enum ConditionType {
+        CONDITION_NULL,
+
         CONDITION_PATH_EXISTS,
         CONDITION_PATH_EXISTS_GLOB,
         CONDITION_PATH_IS_DIRECTORY,
@@ -37,16 +39,18 @@ typedef enum ConditionType {
         CONDITION_DIRECTORY_NOT_EMPTY,
         CONDITION_FILE_NOT_EMPTY,
         CONDITION_FILE_IS_EXECUTABLE,
+
         CONDITION_KERNEL_COMMAND_LINE,
         CONDITION_VIRTUALIZATION,
+        CONDITION_ARCHITECTURE,
         CONDITION_SECURITY,
         CONDITION_CAPABILITY,
         CONDITION_HOST,
         CONDITION_AC_POWER,
-        CONDITION_ARCHITECTURE,
+
         CONDITION_NEEDS_UPDATE,
         CONDITION_FIRST_BOOT,
-        CONDITION_NULL,
+
         _CONDITION_TYPE_MAX,
         _CONDITION_TYPE_INVALID = -1
 } ConditionType;
@@ -61,13 +65,14 @@ typedef enum ConditionResult {
 } ConditionResult;
 
 typedef struct Condition {
-        ConditionType type;
+        ConditionType type:8;
 
         bool trigger:1;
         bool negate:1;
 
+        ConditionResult result:6;
+
         char *parameter;
-        ConditionResult result;
 
         LIST_FIELDS(struct Condition, conditions);
 } Condition;
@@ -78,11 +83,14 @@ void condition_free_list(Condition *c);
 
 int condition_test(Condition *c);
 
-void condition_dump(Condition *c, FILE *f, const char *prefix);
-void condition_dump_list(Condition *c, FILE *f, const char *prefix);
+void condition_dump(Condition *c, FILE *f, const char *prefix, const char *(*to_string)(ConditionType t));
+void condition_dump_list(Condition *c, FILE *f, const char *prefix, const char *(*to_string)(ConditionType t));
 
 const char* condition_type_to_string(ConditionType t) _const_;
-int condition_type_from_string(const char *s) _pure_;
+ConditionType condition_type_from_string(const char *s) _pure_;
+
+const char* assert_type_to_string(ConditionType t) _const_;
+ConditionType assert_type_from_string(const char *s) _pure_;
 
 const char* condition_result_to_string(ConditionResult r) _const_;
 ConditionResult condition_result_from_string(const char *s) _pure_;
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index 8481a9b..8a3e203 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -3271,7 +3271,14 @@ typedef struct UnitStatusInfo {
         bool failed_condition_trigger;
         bool failed_condition_negate;
         const char *failed_condition;
-        const char *failed_condition_param;
+        const char *failed_condition_parameter;
+
+        usec_t assert_timestamp;
+        bool assert_result;
+        bool failed_assert_trigger;
+        bool failed_assert_negate;
+        const char *failed_assert;
+        const char *failed_assert_parameter;
 
         /* Socket */
         unsigned n_accepted;
@@ -3415,7 +3422,8 @@ static void print_status_info(
                 s1 = format_timestamp_relative(since1, sizeof(since1), i->condition_timestamp);
                 s2 = format_timestamp(since2, sizeof(since2), i->condition_timestamp);
 
-                printf("           start condition failed at %s%s%s\n",
+                printf("Condition: start %scondition failed%s at %s%s%s\n",
+                       ansi_highlight_yellow(), ansi_highlight_off(),
                        s2, s1 ? "; " : "", s1 ? s1 : "");
                 if (i->failed_condition_trigger)
                         printf("           none of the trigger conditions were met\n");
@@ -3423,7 +3431,23 @@ static void print_status_info(
                         printf("           %s=%s%s was not met\n",
                                i->failed_condition,
                                i->failed_condition_negate ? "!" : "",
-                               i->failed_condition_param);
+                               i->failed_condition_parameter);
+        }
+
+        if (!i->assert_result && i->assert_timestamp > 0) {
+                s1 = format_timestamp_relative(since1, sizeof(since1), i->assert_timestamp);
+                s2 = format_timestamp(since2, sizeof(since2), i->assert_timestamp);
+
+                printf("   Assert: start %sassertion failed%s at %s%s%s\n",
+                       ansi_highlight_red(), ansi_highlight_off(),
+                       s2, s1 ? "; " : "", s1 ? s1 : "");
+                if (i->failed_assert_trigger)
+                        printf("           none of the trigger assertions were met\n");
+                else if (i->failed_assert)
+                        printf("           %s=%s%s was not met\n",
+                               i->failed_assert,
+                               i->failed_assert_negate ? "!" : "",
+                               i->failed_assert_parameter);
         }
 
         if (i->sysfs_path)
@@ -3674,6 +3698,8 @@ static int status_property(const char *name, sd_bus_message *m, UnitStatusInfo *
                         i->need_daemon_reload = b;
                 else if (streq(name, "ConditionResult"))
                         i->condition_result = b;
+                else if (streq(name, "AssertResult"))
+                        i->assert_result = b;
 
                 break;
         }
@@ -3743,6 +3769,8 @@ static int status_property(const char *name, sd_bus_message *m, UnitStatusInfo *
                         i->active_exit_timestamp = (usec_t) u;
                 else if (streq(name, "ConditionTimestamp"))
                         i->condition_timestamp = (usec_t) u;
+                else if (streq(name, "AssertTimestamp"))
+                        i->assert_timestamp = (usec_t) u;
 
                 break;
         }
@@ -3835,7 +3863,32 @@ static int status_property(const char *name, sd_bus_message *m, UnitStatusInfo *
                                         i->failed_condition = cond;
                                         i->failed_condition_trigger = trigger;
                                         i->failed_condition_negate = negate;
-                                        i->failed_condition_param = param;
+                                        i->failed_condition_parameter = param;
+                                }
+                        }
+                        if (r < 0)
+                                return bus_log_parse_error(r);
+
+                        r = sd_bus_message_exit_container(m);
+                        if (r < 0)
+                                return bus_log_parse_error(r);
+
+                } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "Asserts")) {
+                        const char *cond, *param;
+                        int trigger, negate;
+                        int32_t state;
+
+                        r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sbbsi)");
+                        if (r < 0)
+                                return bus_log_parse_error(r);
+
+                        while ((r = sd_bus_message_read(m, "(sbbsi)", &cond, &trigger, &negate, &param, &state)) > 0) {
+                                log_debug("%s %d %d %s %d", cond, trigger, negate, param, state);
+                                if (state < 0 && (!trigger || !i->failed_assert)) {
+                                        i->failed_assert = cond;
+                                        i->failed_assert_trigger = trigger;
+                                        i->failed_assert_negate = negate;
+                                        i->failed_assert_parameter = param;
                                 }
                         }
                         if (r < 0)
diff --git a/src/test/test-tables.c b/src/test/test-tables.c
index 2138442..97d5609 100644
--- a/src/test/test-tables.c
+++ b/src/test/test-tables.c
@@ -61,6 +61,8 @@ int main(int argc, char **argv) {
         test_table(busname_state, BUSNAME_STATE);
         test_table(cgroup_device_policy, CGROUP_DEVICE_POLICY);
         test_table(condition_type, CONDITION_TYPE);
+        test_table(assert_type, CONDITION_TYPE);
+        test_table(condition_result, CONDITION_RESULT);
         test_table(device_state, DEVICE_STATE);
         test_table(exec_input, EXEC_INPUT);
         test_table(exec_output, EXEC_OUTPUT);

commit cc50ef134b4104cae8783a4ca40b1a70247e3ef9
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Nov 6 02:27:10 2014 +0100

    condition: record test state internally and beef it up to be a full enum

diff --git a/src/core/condition.c b/src/core/condition.c
index d8d1152..8475258 100644
--- a/src/core/condition.c
+++ b/src/core/condition.c
@@ -53,11 +53,9 @@ bool condition_test_list(const char *unit, Condition *first) {
                                        c->trigger ? "|" : "",
                                        c->negate ? "!" : "",
                                        c->parameter,
-                                       r > 0 ? "succeeded" : "failed",
+                                       condition_result_to_string(c->result),
                                        unit);
 
-                c->state = r > 0 ? CONDITION_STATE_SUCCEEDED : CONDITION_STATE_FAILED;
-
                 if (!c->trigger && r <= 0)
                         return false;
 
diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c
index 2d1862c..3fa4271 100644
--- a/src/core/dbus-unit.c
+++ b/src/core/dbus-unit.c
@@ -328,10 +328,16 @@ static int property_get_conditions(
                 return r;
 
         LIST_FOREACH(conditions, c, u->conditions) {
+                int tristate;
+
+                tristate =
+                        c->result == CONDITION_UNTESTED ? 0 :
+                        c->result == CONDITION_SUCCEEDED ? 1 : -1;
+
                 r = sd_bus_message_append(reply, "(sbbsi)",
                                           condition_type_to_string(c->type),
                                           c->trigger, c->negate,
-                                          c->parameter, c->state);
+                                          c->parameter, tristate);
                 if (r < 0)
                         return r;
 
diff --git a/src/shared/condition-util.c b/src/shared/condition-util.c
index 749b577..640a931 100644
--- a/src/shared/condition-util.c
+++ b/src/shared/condition-util.c
@@ -429,17 +429,22 @@ int condition_test(Condition *c) {
                 [CONDITION_FIRST_BOOT] = condition_test_first_boot,
                 [CONDITION_NULL] = condition_test_null,
         };
-        int r;
+
+        int r, b;
 
         assert(c);
         assert(c->type >= 0);
         assert(c->type < _CONDITION_TYPE_MAX);
 
         r = condition_tests[c->type](c);
-        if (r < 0)
+        if (r < 0) {
+                c->result = CONDITION_ERROR;
                 return r;
+        }
 
-        return (r > 0) == !c->negate;
+        b = (r > 0) == !c->negate;
+        c->result = b ? CONDITION_SUCCEEDED : CONDITION_FAILED;
+        return b;
 }
 
 void condition_dump(Condition *c, FILE *f, const char *prefix) {
@@ -456,7 +461,7 @@ void condition_dump(Condition *c, FILE *f, const char *prefix) {
                 c->trigger ? "|" : "",
                 c->negate ? "!" : "",
                 c->parameter,
-                CONDITION_STATE_IS_FAILED(c->state) ? "failed" : CONDITION_STATE_IS_SUCCEEDED(c->state) ? "succeeded" : "untested");
+                condition_result_to_string(c->result));
 }
 
 void condition_dump_list(Condition *first, FILE *f, const char *prefix) {
@@ -489,3 +494,12 @@ static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
 };
 
 DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType);
+
+static const char* const condition_result_table[_CONDITION_RESULT_MAX] = {
+        [CONDITION_UNTESTED] = "untested",
+        [CONDITION_SUCCEEDED] = "succeeded",
+        [CONDITION_FAILED] = "failed",
+        [CONDITION_ERROR] = "error",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(condition_result, ConditionResult);
diff --git a/src/shared/condition-util.h b/src/shared/condition-util.h
index 44f672b..08aee94 100644
--- a/src/shared/condition-util.h
+++ b/src/shared/condition-util.h
@@ -51,15 +51,14 @@ typedef enum ConditionType {
         _CONDITION_TYPE_INVALID = -1
 } ConditionType;
 
-#define CONDITION_STATE_IS_SUCCEEDED(state) ((state) > 0)
-#define CONDITION_STATE_IS_UNKNOWN(state) ((state) == 0)
-#define CONDITION_STATE_IS_FAILED(state) ((state) < 0)
-
-enum {
-        CONDITION_STATE_SUCCEEDED = -1,
-        CONDITION_STATE_UNKNOWN = 0,
-        CONDITION_STATE_FAILED = 1
-};
+typedef enum ConditionResult {
+        CONDITION_UNTESTED,
+        CONDITION_SUCCEEDED,
+        CONDITION_FAILED,
+        CONDITION_ERROR,
+        _CONDITION_RESULT_MAX,
+        _CONDITION_RESULT_INVALID = -1
+} ConditionResult;
 
 typedef struct Condition {
         ConditionType type;
@@ -68,7 +67,7 @@ typedef struct Condition {
         bool negate:1;
 
         char *parameter;
-        int state;
+        ConditionResult result;
 
         LIST_FIELDS(struct Condition, conditions);
 } Condition;
@@ -84,3 +83,6 @@ void condition_dump_list(Condition *c, FILE *f, const char *prefix);
 
 const char* condition_type_to_string(ConditionType t) _const_;
 int condition_type_from_string(const char *s) _pure_;
+
+const char* condition_result_to_string(ConditionResult r) _const_;
+ConditionResult condition_result_from_string(const char *s) _pure_;

commit b80ba1da1c66f570c10a92a40f54a044fb865739
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Nov 6 02:11:08 2014 +0100

    condition: add more test cases

diff --git a/src/shared/condition-util.c b/src/shared/condition-util.c
index 7569b40..749b577 100644
--- a/src/shared/condition-util.c
+++ b/src/shared/condition-util.c
@@ -45,7 +45,9 @@ Condition* condition_new(ConditionType type, const char *parameter, bool trigger
         Condition *c;
         int r;
 
+        assert(type >= 0);
         assert(type < _CONDITION_TYPE_MAX);
+        assert(!parameter == (type == CONDITION_NULL));
 
         c = new0(Condition, 1);
         if (!c)
@@ -397,7 +399,6 @@ static int condition_test_file_is_executable(Condition *c) {
 
 static int condition_test_null(Condition *c) {
         assert(c);
-        assert(c->parameter);
         assert(c->type == CONDITION_NULL);
 
         /* Note that during parsing we already evaluate the string and
diff --git a/src/test/test-condition-util.c b/src/test/test-condition-util.c
index 4f9ae8a..b69e62b 100644
--- a/src/test/test-condition-util.c
+++ b/src/test/test-condition-util.c
@@ -31,6 +31,18 @@ static void test_condition_test_path_exists(void) {
         assert_se(condition_test(condition));
         condition_free(condition);
 
+        condition = condition_new(CONDITION_PATH_EXISTS, "/bin/s?", false, false);
+        assert_se(!condition_test(condition));
+        condition_free(condition);
+
+        condition = condition_new(CONDITION_PATH_EXISTS_GLOB, "/bin/s?", false, false);
+        assert_se(condition_test(condition));
+        condition_free(condition);
+
+        condition = condition_new(CONDITION_PATH_EXISTS_GLOB, "/bin/s?", false, true);
+        assert_se(!condition_test(condition));
+        condition_free(condition);
+
         condition = condition_new(CONDITION_PATH_EXISTS, "/thiscertainlywontexist", false, false);
         assert_se(!condition_test(condition));
         condition_free(condition);
@@ -38,6 +50,38 @@ static void test_condition_test_path_exists(void) {
         condition = condition_new(CONDITION_PATH_EXISTS, "/thiscertainlywontexist", false, true);
         assert_se(condition_test(condition));
         condition_free(condition);
+
+        condition = condition_new(CONDITION_PATH_IS_DIRECTORY, "/bin", false, false);
+        assert_se(condition_test(condition));
+        condition_free(condition);
+
+        condition = condition_new(CONDITION_DIRECTORY_NOT_EMPTY, "/bin", false, false);
+        assert_se(condition_test(condition));
+        condition_free(condition);
+
+        condition = condition_new(CONDITION_FILE_NOT_EMPTY, "/bin/sh", false, false);
+        assert_se(condition_test(condition));
+        condition_free(condition);
+
+        condition = condition_new(CONDITION_FILE_IS_EXECUTABLE, "/bin/sh", false, false);
+        assert_se(condition_test(condition));
+        condition_free(condition);
+
+        condition = condition_new(CONDITION_FILE_IS_EXECUTABLE, "/etc/passwd", false, false);
+        assert_se(!condition_test(condition));
+        condition_free(condition);
+
+        condition = condition_new(CONDITION_PATH_IS_MOUNT_POINT, "/proc", false, false);
+        assert_se(condition_test(condition));
+        condition_free(condition);
+
+        condition = condition_new(CONDITION_PATH_IS_MOUNT_POINT, "/", false, false);
+        assert_se(condition_test(condition));
+        condition_free(condition);
+
+        condition = condition_new(CONDITION_PATH_IS_MOUNT_POINT, "/bin", false, false);
+        assert_se(!condition_test(condition));
+        condition_free(condition);
 }
 
 static void test_condition_test_ac_power(void) {
@@ -123,6 +167,18 @@ static void test_condition_test_kernel_command_line(void) {
         condition_free(condition);
 }
 
+static void test_condition_test_null(void) {
+        Condition *condition;
+
+        condition = condition_new(CONDITION_NULL, NULL, false, false);
+        assert_se(condition_test(condition));
+        condition_free(condition);
+
+        condition = condition_new(CONDITION_NULL, NULL, false, true);
+        assert_se(!condition_test(condition));
+        condition_free(condition);
+}
+
 int main(int argc, char *argv[]) {
         log_parse_environment();
         log_open();
@@ -132,6 +188,7 @@ int main(int argc, char *argv[]) {
         test_condition_test_host();
         test_condition_test_architecture();
         test_condition_test_kernel_command_line();
+        test_condition_test_null();
 
         return 0;
 }

commit 124aff6251c095367ce1323a21fa23235cbb0490
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Nov 6 02:10:56 2014 +0100

    journal: adjust audit log messages a bit

diff --git a/src/journal/journald-audit.c b/src/journal/journald-audit.c
index f543732..341d748 100644
--- a/src/journal/journald-audit.c
+++ b/src/journal/journald-audit.c
@@ -396,8 +396,8 @@ static void process_audit_string(Server *s, int type, const char *data, size_t s
         sprintf(id_field, "_AUDIT_ID=%" PRIu64, id);
         IOVEC_SET_STRING(iov[n_iov++], id_field);
 
-        m = alloca(strlen("MESSAGE=audit-") + DECIMAL_STR_MAX(int) + strlen(": ") + strlen(p) + 1);
-        sprintf(m, "MESSAGE=audit-%i: %s", type, p);
+        m = alloca(strlen("MESSAGE=<audit-") + DECIMAL_STR_MAX(int) + strlen("> ") + strlen(p) + 1);
+        sprintf(m, "MESSAGE=<audit-%i> %s", type, p);
         IOVEC_SET_STRING(iov[n_iov++], m);
 
         z = n_iov;

commit a4705396adbf1a8a3e52ed133d9b1f12cb13e77e
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Nov 6 02:02:13 2014 +0100

    condition: internalize condition test functions
    
    Also, implement the negation check inside of condition_test() instead of
    individually in each test function.

diff --git a/Makefile.am b/Makefile.am
index 452f07c..c5667c3 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -3488,7 +3488,8 @@ systemd_udevd_SOURCES = \
 	src/udev/udevd.c
 
 systemd_udevd_LDADD = \
-	libudev-core.la
+	libudev-core.la \
+	libsystemd-capability.la
 
 udevadm_SOURCES = \
 	src/udev/udevadm.c \
@@ -3502,7 +3503,8 @@ udevadm_SOURCES = \
 	src/udev/udevadm-test-builtin.c
 
 udevadm_LDADD = \
-	libudev-core.la
+	libudev-core.la \
+	libsystemd-capability.la
 
 # Update hwdb on installation. Do not bother if installing
 # in DESTDIR, since this is likely for packaging purposes.
@@ -3536,7 +3538,8 @@ test_udev_SOURCES = \
 	src/test/test-udev.c
 
 test_udev_LDADD = \
-	libudev-core.la \
+	libudev-core.la  \
+	libsystemd-capability.la \
 	$(BLKID_LIBS) \
 	$(KMOD_LIBS) \
 	$(SELINUX_LIBS)
@@ -5210,11 +5213,9 @@ networkctl_LDADD = \
 test_network_SOURCES = \
 	src/network/test-network.c
 
-test_network_CFLAGS = \
-	$(AM_CFLAGS)
-
 test_network_LDADD = \
-	libsystemd-networkd-core.la
+	libsystemd-networkd-core.la \
+	libsystemd-capability.la
 
 test_network_tables_SOURCES = \
 	src/network/test-network-tables.c \
diff --git a/src/libsystemd-network/network-internal.c b/src/libsystemd-network/network-internal.c
index 372f3ed..6f16050 100644
--- a/src/libsystemd-network/network-internal.c
+++ b/src/libsystemd-network/network-internal.c
@@ -98,16 +98,16 @@ bool net_match_config(const struct ether_addr *match_mac,
                       const char *dev_type,
                       const char *dev_name) {
 
-        if (match_host && !condition_test_host(match_host))
+        if (match_host && !condition_test(match_host))
                 return 0;
 
-        if (match_virt && !condition_test_virtualization(match_virt))
+        if (match_virt && !condition_test(match_virt))
                 return 0;
 
-        if (match_kernel && !condition_test_kernel_command_line(match_kernel))
+        if (match_kernel && !condition_test(match_kernel))
                 return 0;
 
-        if (match_arch && !condition_test_architecture(match_arch))
+        if (match_arch && !condition_test(match_arch))
                 return 0;
 
         if (match_mac && (!dev_mac || memcmp(match_mac, dev_mac, ETH_ALEN)))
diff --git a/src/shared/condition-util.c b/src/shared/condition-util.c
index ab889e3..7569b40 100644
--- a/src/shared/condition-util.c
+++ b/src/shared/condition-util.c
@@ -78,7 +78,7 @@ void condition_free_list(Condition *first) {
                 condition_free(c);
 }
 
-int condition_test_kernel_command_line(Condition *c) {
+static int condition_test_kernel_command_line(Condition *c) {
         _cleanup_free_ char *line = NULL;
         const char *p;
         bool equal;
@@ -92,7 +92,7 @@ int condition_test_kernel_command_line(Condition *c) {
         if (r < 0)
                 return r;
         if (r == 0)
-                return c->negate;
+                return false;
 
         equal = !!strchr(c->parameter, '=');
         p = line;
@@ -105,7 +105,7 @@ int condition_test_kernel_command_line(Condition *c) {
                 if (r < 0)
                         return r;
                 if (r == 0)
-                        return c->negate;
+                        break;
 
                 if (equal)
                         found = streq(word, c->parameter);
@@ -117,13 +117,13 @@ int condition_test_kernel_command_line(Condition *c) {
                 }
 
                 if (found)
-                        return !c->negate;
+                        return true;
         }
 
-        return c->negate;
+        return false;
 }
 
-int condition_test_virtualization(Condition *c) {
+static int condition_test_virtualization(Condition *c) {
         int b, v;
         const char *id;
 
@@ -139,23 +139,23 @@ int condition_test_virtualization(Condition *c) {
         b = parse_boolean(c->parameter);
 
         if (v > 0 && b > 0)
-                return !c->negate;
+                return true;
 
         if (v == 0 && b == 0)
-                return !c->negate;
+                return true;
 
         /* Then, compare categorization */
         if (v == VIRTUALIZATION_VM && streq(c->parameter, "vm"))
-                return !c->negate;
+                return true;
 
         if (v == VIRTUALIZATION_CONTAINER && streq(c->parameter, "container"))
-                return !c->negate;
+                return true;
 
         /* Finally compare id */
-        return (v > 0 && streq(c->parameter, id)) == !c->negate;
+        return v > 0 && streq(c->parameter, id);
 }
 
-int condition_test_architecture(Condition *c) {
+static int condition_test_architecture(Condition *c) {
         int a, b;
 
         assert(c);
@@ -173,10 +173,10 @@ int condition_test_architecture(Condition *c) {
         if (b < 0)
                 return b;
 
-        return (a == b) == !c->negate;
+        return a == b;
 }
 
-int condition_test_host(Condition *c) {
+static int condition_test_host(Condition *c) {
         _cleanup_free_ char *h = NULL;
         sd_id128_t x, y;
         int r;
@@ -191,17 +191,17 @@ int condition_test_host(Condition *c) {
                 if (r < 0)
                         return r;
 
-                return sd_id128_equal(x, y) == !c->negate;
+                return sd_id128_equal(x, y);
         }
 
         h = gethostname_malloc();
         if (!h)
                 return -ENOMEM;
 
-        return (fnmatch(c->parameter, h, FNM_CASEFOLD) == 0) == !c->negate;
+        return fnmatch(c->parameter, h, FNM_CASEFOLD) == 0;
 }
 
-int condition_test_ac_power(Condition *c) {
+static int condition_test_ac_power(Condition *c) {
         int r;
 
         assert(c);
@@ -212,7 +212,7 @@ int condition_test_ac_power(Condition *c) {
         if (r < 0)
                 return r;
 
-        return ((on_ac_power() != 0) == !!r) == !c->negate;
+        return (on_ac_power() != 0) == !!r;
 }
 
 static int condition_test_security(Condition *c) {
@@ -221,17 +221,17 @@ static int condition_test_security(Condition *c) {
         assert(c->type == CONDITION_SECURITY);
 
         if (streq(c->parameter, "selinux"))
-                return mac_selinux_use() == !c->negate;
+                return mac_selinux_use();
         if (streq(c->parameter, "smack"))
-                return mac_smack_use() == !c->negate;
+                return mac_smack_use();
         if (streq(c->parameter, "apparmor"))
-                return mac_apparmor_use() == !c->negate;
+                return mac_apparmor_use();
         if (streq(c->parameter, "audit"))
-                return use_audit() == !c->negate;
+                return use_audit();
         if (streq(c->parameter, "ima"))
-                return use_ima() == !c->negate;
+                return use_ima();
 
-        return c->negate;
+        return false;
 }
 
 static int condition_test_capability(Condition *c) {
@@ -265,7 +265,7 @@ static int condition_test_capability(Condition *c) {
                 }
         }
 
-        return !!(capabilities & (1ULL << value)) == !c->negate;
+        return !!(capabilities & (1ULL << value));
 }
 
 static int condition_test_needs_update(Condition *c) {
@@ -278,24 +278,24 @@ static int condition_test_needs_update(Condition *c) {
 
         /* If the file system is read-only we shouldn't suggest an update */
         if (path_is_read_only_fs(c->parameter) > 0)
-                return c->negate;
+                return false;
 
         /* Any other failure means we should allow the condition to be true,
          * so that we rather invoke too many update tools then too
          * few. */
 
         if (!path_is_absolute(c->parameter))
-                return !c->negate;
+                return true;
 
         p = strappenda(c->parameter, "/.updated");
         if (lstat(p, &other) < 0)
-                return !c->negate;
+                return true;
 
         if (lstat("/usr/", &usr) < 0)
-                return !c->negate;
+                return true;
 
-        return (usr.st_mtim.tv_sec > other.st_mtim.tv_sec ||
-                (usr.st_mtim.tv_sec == other.st_mtim.tv_sec && usr.st_mtim.tv_nsec > other.st_mtim.tv_nsec)) == !c->negate;
+        return usr.st_mtim.tv_sec > other.st_mtim.tv_sec ||
+                (usr.st_mtim.tv_sec == other.st_mtim.tv_sec && usr.st_mtim.tv_nsec > other.st_mtim.tv_nsec);
 }
 
 static int condition_test_first_boot(Condition *c) {
@@ -309,7 +309,7 @@ static int condition_test_first_boot(Condition *c) {
         if (r < 0)
                 return r;
 
-        return ((access("/run/systemd/first-boot", F_OK) >= 0) == !!r) == !c->negate;
+        return (access("/run/systemd/first-boot", F_OK) >= 0) == !!r;
 }
 
 static int condition_test_path_exists(Condition *c) {
@@ -317,7 +317,7 @@ static int condition_test_path_exists(Condition *c) {
         assert(c->parameter);
         assert(c->type == CONDITION_PATH_EXISTS);
 
-        return (access(c->parameter, F_OK) >= 0) == !c->negate;
+        return access(c->parameter, F_OK) >= 0;
 }
 
 static int condition_test_path_exists_glob(Condition *c) {
@@ -325,7 +325,7 @@ static int condition_test_path_exists_glob(Condition *c) {
         assert(c->parameter);
         assert(c->type == CONDITION_PATH_EXISTS_GLOB);
 
-        return (glob_exists(c->parameter) > 0) == !c->negate;
+        return glob_exists(c->parameter) > 0;
 }
 
 static int condition_test_path_is_directory(Condition *c) {
@@ -333,7 +333,7 @@ static int condition_test_path_is_directory(Condition *c) {
         assert(c->parameter);
         assert(c->type == CONDITION_PATH_IS_DIRECTORY);
 
-        return (is_dir(c->parameter, true) > 0) == !c->negate;
+        return is_dir(c->parameter, true) > 0;
 }
 
 static int condition_test_path_is_symbolic_link(Condition *c) {
@@ -341,7 +341,7 @@ static int condition_test_path_is_symbolic_link(Condition *c) {
         assert(c->parameter);
         assert(c->type == CONDITION_PATH_IS_SYMBOLIC_LINK);
 
-        return (is_symlink(c->parameter) > 0) == !c->negate;
+        return is_symlink(c->parameter) > 0;
 }
 
 static int condition_test_path_is_mount_point(Condition *c) {
@@ -349,7 +349,7 @@ static int condition_test_path_is_mount_point(Condition *c) {
         assert(c->parameter);
         assert(c->type == CONDITION_PATH_IS_MOUNT_POINT);
 
-        return (path_is_mount_point(c->parameter, true) > 0) == !c->negate;
+        return path_is_mount_point(c->parameter, true) > 0;
 }
 
 static int condition_test_path_is_read_write(Condition *c) {
@@ -357,7 +357,7 @@ static int condition_test_path_is_read_write(Condition *c) {
         assert(c->parameter);
         assert(c->type == CONDITION_PATH_IS_READ_WRITE);
 
-        return (path_is_read_only_fs(c->parameter) > 0) == c->negate;
+        return path_is_read_only_fs(c->parameter) <= 0;
 }
 
 static int condition_test_directory_not_empty(Condition *c) {
@@ -368,7 +368,7 @@ static int condition_test_directory_not_empty(Condition *c) {
         assert(c->type == CONDITION_DIRECTORY_NOT_EMPTY);
 
         r = dir_is_empty(c->parameter);
-        return !(r == -ENOENT || r > 0) == !c->negate;
+        return r <= 0 && r != -ENOENT;
 }
 
 static int condition_test_file_not_empty(Condition *c) {
@@ -380,7 +380,7 @@ static int condition_test_file_not_empty(Condition *c) {
 
         return (stat(c->parameter, &st) >= 0 &&
                 S_ISREG(st.st_mode) &&
-                st.st_size > 0) == !c->negate;
+                st.st_size > 0);
 }
 
 static int condition_test_file_is_executable(Condition *c) {
@@ -392,7 +392,7 @@ static int condition_test_file_is_executable(Condition *c) {
 
         return (stat(c->parameter, &st) >= 0 &&
                 S_ISREG(st.st_mode) &&
-                (st.st_mode & 0111)) == !c->negate;
+                (st.st_mode & 0111));
 }
 
 static int condition_test_null(Condition *c) {
@@ -402,7 +402,7 @@ static int condition_test_null(Condition *c) {
 
         /* Note that during parsing we already evaluate the string and
          * store it in c->negate */
-        return !c->negate;
+        return true;
 }
 
 int condition_test(Condition *c) {
@@ -428,12 +428,17 @@ int condition_test(Condition *c) {
                 [CONDITION_FIRST_BOOT] = condition_test_first_boot,
                 [CONDITION_NULL] = condition_test_null,
         };
+        int r;
 
         assert(c);
         assert(c->type >= 0);
         assert(c->type < _CONDITION_TYPE_MAX);
 
-        return condition_tests[c->type](c);
+        r = condition_tests[c->type](c);
+        if (r < 0)
+                return r;
+
+        return (r > 0) == !c->negate;
 }
 
 void condition_dump(Condition *c, FILE *f, const char *prefix) {
diff --git a/src/shared/condition-util.h b/src/shared/condition-util.h
index deeb6b4..44f672b 100644
--- a/src/shared/condition-util.h
+++ b/src/shared/condition-util.h
@@ -77,12 +77,6 @@ Condition* condition_new(ConditionType type, const char *parameter, bool trigger
 void condition_free(Condition *c);
 void condition_free_list(Condition *c);
 
-int condition_test_kernel_command_line(Condition *c);
-int condition_test_virtualization(Condition *c);
-int condition_test_architecture(Condition *c);
-int condition_test_host(Condition *c);
-int condition_test_ac_power(Condition *c);
-
 int condition_test(Condition *c);
 
 void condition_dump(Condition *c, FILE *f, const char *prefix);

commit d1bddcec98551ea748f39a742a4cbcf9ea9254ef
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Nov 6 01:40:37 2014 +0100

    condition: unify condition logic in one file

diff --git a/Makefile.am b/Makefile.am
index f614b86..452f07c 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1524,8 +1524,9 @@ test_condition_util_SOURCES = \
 
 test_condition_util_LDADD = \
 	libsystemd-shared.la \
-	libsystemd-internal.la
-
+	libsystemd-internal.la \
+	libsystemd-capability.la \
+	libsystemd-label.la
 
 test_fdset_SOURCES = \
 	src/test/test-fdset.c
diff --git a/src/core/condition.c b/src/core/condition.c
index 32135ec..d8d1152 100644
--- a/src/core/condition.c
+++ b/src/core/condition.c
@@ -19,216 +19,8 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#include <stdlib.h>
-#include <errno.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/capability.h>
-#include <sys/statvfs.h>
-#include <fnmatch.h>
-
-#include "sd-id128.h"
-#include "util.h"
 #include "condition.h"
-#include "virt.h"
-#include "path-util.h"
-#include "fileio.h"
 #include "unit.h"
-#include "smack-util.h"
-#include "apparmor-util.h"
-#include "ima-util.h"
-#include "selinux-util.h"
-#include "audit.h"
-
-static int condition_test_security(Condition *c) {
-        assert(c);
-        assert(c->parameter);
-        assert(c->type == CONDITION_SECURITY);
-
-        if (streq(c->parameter, "selinux"))
-                return mac_selinux_use() == !c->negate;
-        if (streq(c->parameter, "smack"))
-                return mac_smack_use() == !c->negate;
-        if (streq(c->parameter, "apparmor"))
-                return mac_apparmor_use() == !c->negate;
-        if (streq(c->parameter, "audit"))
-                return use_audit() == !c->negate;
-        if (streq(c->parameter, "ima"))
-                return use_ima() == !c->negate;
-
-        return c->negate;
-}
-
-static int condition_test_capability(Condition *c) {
-        _cleanup_fclose_ FILE *f = NULL;
-        cap_value_t value;
-        char line[LINE_MAX];
-        unsigned long long capabilities = -1;
-
-        assert(c);
-        assert(c->parameter);
-        assert(c->type == CONDITION_CAPABILITY);
-
-        /* If it's an invalid capability, we don't have it */
-
-        if (cap_from_name(c->parameter, &value) < 0)
-                return -EINVAL;
-
-        /* If it's a valid capability we default to assume
-         * that we have it */
-
-        f = fopen("/proc/self/status", "re");
-        if (!f)
-                return -errno;
-
-        while (fgets(line, sizeof(line), f)) {
-                truncate_nl(line);
-
-                if (startswith(line, "CapBnd:")) {
-                        (void) sscanf(line+7, "%llx", &capabilities);
-                        break;
-                }
-        }
-
-        return !!(capabilities & (1ULL << value)) == !c->negate;
-}
-
-static bool condition_test_needs_update(Condition *c) {
-        const char *p;
-        struct stat usr, other;
-
-        assert(c);
-        assert(c->parameter);
-        assert(c->type == CONDITION_NEEDS_UPDATE);
-
-        /* If the file system is read-only we shouldn't suggest an update */
-        if (path_is_read_only_fs(c->parameter) > 0)
-                return c->negate;
-
-        /* Any other failure means we should allow the condition to be true,
-         * so that we rather invoke too many update tools then too
-         * few. */
-
-        if (!path_is_absolute(c->parameter))
-                return !c->negate;
-
-        p = strappenda(c->parameter, "/.updated");
-        if (lstat(p, &other) < 0)
-                return !c->negate;
-
-        if (lstat("/usr/", &usr) < 0)
-                return !c->negate;
-
-        return (usr.st_mtim.tv_sec > other.st_mtim.tv_sec ||
-                (usr.st_mtim.tv_sec == other.st_mtim.tv_sec && usr.st_mtim.tv_nsec > other.st_mtim.tv_nsec)) == !c->negate;
-}
-
-static bool condition_test_first_boot(Condition *c) {
-        int r;
-
-        assert(c);
-        assert(c->parameter);
-        assert(c->type == CONDITION_FIRST_BOOT);
-
-        r = parse_boolean(c->parameter);
-        if (r < 0)
-                return r;
-
-        return ((access("/run/systemd/first-boot", F_OK) >= 0) == !!r) == !c->negate;
-}
-
-static int condition_test(Condition *c) {
-        assert(c);
-
-        switch(c->type) {
-
-        case CONDITION_PATH_EXISTS:
-                return (access(c->parameter, F_OK) >= 0) == !c->negate;
-
-        case CONDITION_PATH_EXISTS_GLOB:
-                return (glob_exists(c->parameter) > 0) == !c->negate;
-
-        case CONDITION_PATH_IS_DIRECTORY: {
-                struct stat st;
-
-                if (stat(c->parameter, &st) < 0)
-                        return c->negate;
-                return S_ISDIR(st.st_mode) == !c->negate;
-        }
-
-        case CONDITION_PATH_IS_SYMBOLIC_LINK: {
-                struct stat st;
-
-                if (lstat(c->parameter, &st) < 0)
-                        return c->negate;
-                return S_ISLNK(st.st_mode) == !c->negate;
-        }
-
-        case CONDITION_PATH_IS_MOUNT_POINT:
-                return (path_is_mount_point(c->parameter, true) > 0) == !c->negate;
-
-        case CONDITION_PATH_IS_READ_WRITE:
-                return (path_is_read_only_fs(c->parameter) > 0) == c->negate;
-
-        case CONDITION_DIRECTORY_NOT_EMPTY: {
-                int k;
-
-                k = dir_is_empty(c->parameter);
-                return !(k == -ENOENT || k > 0) == !c->negate;
-        }
-
-        case CONDITION_FILE_NOT_EMPTY: {
-                struct stat st;
-
-                if (stat(c->parameter, &st) < 0)
-                        return c->negate;
-
-                return (S_ISREG(st.st_mode) && st.st_size > 0) == !c->negate;
-        }
-
-        case CONDITION_FILE_IS_EXECUTABLE: {
-                struct stat st;
-
-                if (stat(c->parameter, &st) < 0)
-                        return c->negate;
-
-                return (S_ISREG(st.st_mode) && (st.st_mode & 0111)) == !c->negate;
-        }
-
-        case CONDITION_KERNEL_COMMAND_LINE:
-                return condition_test_kernel_command_line(c);
-
-        case CONDITION_VIRTUALIZATION:
-                return condition_test_virtualization(c);
-
-        case CONDITION_SECURITY:
-                return condition_test_security(c);
-
-        case CONDITION_CAPABILITY:
-                return condition_test_capability(c);
-
-        case CONDITION_HOST:
-                return condition_test_host(c);
-
-        case CONDITION_AC_POWER:
-                return condition_test_ac_power(c);
-
-        case CONDITION_ARCHITECTURE:
-                return condition_test_architecture(c);
-
-        case CONDITION_NEEDS_UPDATE:
-                return condition_test_needs_update(c);
-
-        case CONDITION_FIRST_BOOT:
-                return condition_test_first_boot(c);
-
-        case CONDITION_NULL:
-                return !c->negate;
-
-        default:
-                assert_not_reached("Invalid condition type.");
-        }
-}
 
 bool condition_test_list(const char *unit, Condition *first) {
         Condition *c;
diff --git a/src/shared/condition-util.c b/src/shared/condition-util.c
index 33752c8..ab889e3 100644
--- a/src/shared/condition-util.c
+++ b/src/shared/condition-util.c
@@ -26,7 +26,7 @@
 #include <sys/statvfs.h>
 #include <fnmatch.h>
 
-#include "systemd/sd-id128.h"
+#include "sd-id128.h"
 #include "util.h"
 #include "condition-util.h"
 #include "virt.h"
@@ -34,9 +34,16 @@
 #include "fileio.h"
 #include "unit.h"
 #include "architecture.h"
+#include "virt.h"
+#include "smack-util.h"
+#include "apparmor-util.h"
+#include "ima-util.h"
+#include "selinux-util.h"
+#include "audit.h"
 
 Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate) {
         Condition *c;
+        int r;
 
         assert(type < _CONDITION_TYPE_MAX);
 
@@ -48,12 +55,10 @@ Condition* condition_new(ConditionType type, const char *parameter, bool trigger
         c->trigger = trigger;
         c->negate = negate;
 
-        if (parameter) {
-                c->parameter = strdup(parameter);
-                if (!c->parameter) {
-                        free(c);
-                        return NULL;
-                }
+        r = free_and_strdup(&c->parameter, parameter);
+        if (r < 0) {
+                free(c);
+                return NULL;
         }
 
         return c;
@@ -210,6 +215,227 @@ int condition_test_ac_power(Condition *c) {
         return ((on_ac_power() != 0) == !!r) == !c->negate;
 }
 
+static int condition_test_security(Condition *c) {
+        assert(c);
+        assert(c->parameter);
+        assert(c->type == CONDITION_SECURITY);
+
+        if (streq(c->parameter, "selinux"))
+                return mac_selinux_use() == !c->negate;
+        if (streq(c->parameter, "smack"))
+                return mac_smack_use() == !c->negate;
+        if (streq(c->parameter, "apparmor"))
+                return mac_apparmor_use() == !c->negate;
+        if (streq(c->parameter, "audit"))
+                return use_audit() == !c->negate;
+        if (streq(c->parameter, "ima"))
+                return use_ima() == !c->negate;
+
+        return c->negate;
+}
+
+static int condition_test_capability(Condition *c) {
+        _cleanup_fclose_ FILE *f = NULL;
+        cap_value_t value;
+        char line[LINE_MAX];
+        unsigned long long capabilities = -1;
+
+        assert(c);
+        assert(c->parameter);
+        assert(c->type == CONDITION_CAPABILITY);
+
+        /* If it's an invalid capability, we don't have it */
+
+        if (cap_from_name(c->parameter, &value) < 0)
+                return -EINVAL;
+
+        /* If it's a valid capability we default to assume
+         * that we have it */
+
+        f = fopen("/proc/self/status", "re");
+        if (!f)
+                return -errno;
+
+        while (fgets(line, sizeof(line), f)) {
+                truncate_nl(line);
+
+                if (startswith(line, "CapBnd:")) {
+                        (void) sscanf(line+7, "%llx", &capabilities);
+                        break;
+                }
+        }
+
+        return !!(capabilities & (1ULL << value)) == !c->negate;
+}
+
+static int condition_test_needs_update(Condition *c) {
+        const char *p;
+        struct stat usr, other;
+
+        assert(c);
+        assert(c->parameter);
+        assert(c->type == CONDITION_NEEDS_UPDATE);
+
+        /* If the file system is read-only we shouldn't suggest an update */
+        if (path_is_read_only_fs(c->parameter) > 0)
+                return c->negate;
+
+        /* Any other failure means we should allow the condition to be true,
+         * so that we rather invoke too many update tools then too
+         * few. */
+
+        if (!path_is_absolute(c->parameter))
+                return !c->negate;
+
+        p = strappenda(c->parameter, "/.updated");
+        if (lstat(p, &other) < 0)
+                return !c->negate;
+
+        if (lstat("/usr/", &usr) < 0)
+                return !c->negate;
+
+        return (usr.st_mtim.tv_sec > other.st_mtim.tv_sec ||
+                (usr.st_mtim.tv_sec == other.st_mtim.tv_sec && usr.st_mtim.tv_nsec > other.st_mtim.tv_nsec)) == !c->negate;
+}
+
+static int condition_test_first_boot(Condition *c) {
+        int r;
+
+        assert(c);
+        assert(c->parameter);
+        assert(c->type == CONDITION_FIRST_BOOT);
+
+        r = parse_boolean(c->parameter);
+        if (r < 0)
+                return r;
+
+        return ((access("/run/systemd/first-boot", F_OK) >= 0) == !!r) == !c->negate;
+}
+
+static int condition_test_path_exists(Condition *c) {
+        assert(c);
+        assert(c->parameter);
+        assert(c->type == CONDITION_PATH_EXISTS);
+
+        return (access(c->parameter, F_OK) >= 0) == !c->negate;
+}
+
+static int condition_test_path_exists_glob(Condition *c) {
+        assert(c);
+        assert(c->parameter);
+        assert(c->type == CONDITION_PATH_EXISTS_GLOB);
+
+        return (glob_exists(c->parameter) > 0) == !c->negate;
+}
+
+static int condition_test_path_is_directory(Condition *c) {
+        assert(c);
+        assert(c->parameter);
+        assert(c->type == CONDITION_PATH_IS_DIRECTORY);
+
+        return (is_dir(c->parameter, true) > 0) == !c->negate;
+}
+
+static int condition_test_path_is_symbolic_link(Condition *c) {
+        assert(c);
+        assert(c->parameter);
+        assert(c->type == CONDITION_PATH_IS_SYMBOLIC_LINK);
+
+        return (is_symlink(c->parameter) > 0) == !c->negate;
+}
+
+static int condition_test_path_is_mount_point(Condition *c) {
+        assert(c);
+        assert(c->parameter);
+        assert(c->type == CONDITION_PATH_IS_MOUNT_POINT);
+
+        return (path_is_mount_point(c->parameter, true) > 0) == !c->negate;
+}
+
+static int condition_test_path_is_read_write(Condition *c) {
+        assert(c);
+        assert(c->parameter);
+        assert(c->type == CONDITION_PATH_IS_READ_WRITE);
+
+        return (path_is_read_only_fs(c->parameter) > 0) == c->negate;
+}
+
+static int condition_test_directory_not_empty(Condition *c) {
+        int r;
+
+        assert(c);
+        assert(c->parameter);
+        assert(c->type == CONDITION_DIRECTORY_NOT_EMPTY);
+
+        r = dir_is_empty(c->parameter);
+        return !(r == -ENOENT || r > 0) == !c->negate;
+}
+
+static int condition_test_file_not_empty(Condition *c) {
+        struct stat st;
+
+        assert(c);
+        assert(c->parameter);
+        assert(c->type == CONDITION_FILE_NOT_EMPTY);
+
+        return (stat(c->parameter, &st) >= 0 &&
+                S_ISREG(st.st_mode) &&
+                st.st_size > 0) == !c->negate;
+}
+
+static int condition_test_file_is_executable(Condition *c) {
+        struct stat st;
+
+        assert(c);
+        assert(c->parameter);
+        assert(c->type == CONDITION_FILE_IS_EXECUTABLE);
+
+        return (stat(c->parameter, &st) >= 0 &&
+                S_ISREG(st.st_mode) &&
+                (st.st_mode & 0111)) == !c->negate;
+}
+
+static int condition_test_null(Condition *c) {
+        assert(c);
+        assert(c->parameter);
+        assert(c->type == CONDITION_NULL);
+
+        /* Note that during parsing we already evaluate the string and
+         * store it in c->negate */
+        return !c->negate;
+}
+
+int condition_test(Condition *c) {
+
+        static int (*const condition_tests[_CONDITION_TYPE_MAX])(Condition *c) = {
+                [CONDITION_PATH_EXISTS] = condition_test_path_exists,
+                [CONDITION_PATH_EXISTS_GLOB] = condition_test_path_exists_glob,
+                [CONDITION_PATH_IS_DIRECTORY] = condition_test_path_is_directory,
+                [CONDITION_PATH_IS_SYMBOLIC_LINK] = condition_test_path_is_symbolic_link,
+                [CONDITION_PATH_IS_MOUNT_POINT] = condition_test_path_is_mount_point,
+                [CONDITION_PATH_IS_READ_WRITE] = condition_test_path_is_read_write,
+                [CONDITION_DIRECTORY_NOT_EMPTY] = condition_test_directory_not_empty,
+                [CONDITION_FILE_NOT_EMPTY] = condition_test_file_not_empty,
+                [CONDITION_FILE_IS_EXECUTABLE] = condition_test_file_is_executable,
+                [CONDITION_KERNEL_COMMAND_LINE] = condition_test_kernel_command_line,
+                [CONDITION_VIRTUALIZATION] = condition_test_virtualization,
+                [CONDITION_SECURITY] = condition_test_security,
+                [CONDITION_CAPABILITY] = condition_test_capability,
+                [CONDITION_HOST] = condition_test_host,
+                [CONDITION_AC_POWER] = condition_test_ac_power,
+                [CONDITION_ARCHITECTURE] = condition_test_architecture,
+                [CONDITION_NEEDS_UPDATE] = condition_test_needs_update,
+                [CONDITION_FIRST_BOOT] = condition_test_first_boot,
+                [CONDITION_NULL] = condition_test_null,
+        };
+
+        assert(c);
+        assert(c->type >= 0);
+        assert(c->type < _CONDITION_TYPE_MAX);
+
+        return condition_tests[c->type](c);
+}
+
 void condition_dump(Condition *c, FILE *f, const char *prefix) {
         assert(c);
         assert(f);
diff --git a/src/shared/condition-util.h b/src/shared/condition-util.h
index fbb8150..deeb6b4 100644
--- a/src/shared/condition-util.h
+++ b/src/shared/condition-util.h
@@ -83,6 +83,8 @@ int condition_test_architecture(Condition *c);
 int condition_test_host(Condition *c);
 int condition_test_ac_power(Condition *c);
 
+int condition_test(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/shared/util.c b/src/shared/util.c
index d33f349..39ce46a 100644
--- a/src/shared/util.c
+++ b/src/shared/util.c
@@ -6987,14 +6987,14 @@ int is_symlink(const char *path) {
 
 int is_dir(const char* path, bool follow) {
         struct stat st;
+        int r;
 
-        if (follow) {
-                if (stat(path, &st) < 0)
-                        return -errno;
-        } else {
-                if (lstat(path, &st) < 0)
-                        return -errno;
-        }
+        if (follow)
+                r = stat(path, &st);
+        else
+                r = lstat(path, &st);
+        if (r < 0)
+                return -errno;
 
         return !!S_ISDIR(st.st_mode);
 }
diff --git a/src/test/test-condition-util.c b/src/test/test-condition-util.c
index 7a247fb..4f9ae8a 100644
--- a/src/test/test-condition-util.c
+++ b/src/test/test-condition-util.c
@@ -22,21 +22,37 @@
 #include "util.h"
 #include "log.h"
 #include "architecture.h"
-#include "systemd/sd-id128.h"
+#include "sd-id128.h"
+
+static void test_condition_test_path_exists(void) {
+        Condition *condition;
+
+        condition = condition_new(CONDITION_PATH_EXISTS, "/bin/sh", false, false);
+        assert_se(condition_test(condition));
+        condition_free(condition);
+
+        condition = condition_new(CONDITION_PATH_EXISTS, "/thiscertainlywontexist", false, false);
+        assert_se(!condition_test(condition));
+        condition_free(condition);
+
+        condition = condition_new(CONDITION_PATH_EXISTS, "/thiscertainlywontexist", false, true);
+        assert_se(condition_test(condition));
+        condition_free(condition);
+}
 
 static void test_condition_test_ac_power(void) {
         Condition *condition;
 
         condition = condition_new(CONDITION_AC_POWER, "true", false, false);
-        assert_se(condition_test_ac_power(condition) == on_ac_power());
+        assert_se(condition_test(condition) == on_ac_power());
         condition_free(condition);
 
         condition = condition_new(CONDITION_AC_POWER, "false", false, false);
-        assert_se(condition_test_ac_power(condition) != on_ac_power());
+        assert_se(condition_test(condition) != on_ac_power());
         condition_free(condition);
 
         condition = condition_new(CONDITION_AC_POWER, "false", false, true);
-        assert_se(condition_test_ac_power(condition) == on_ac_power());
+        assert_se(condition_test(condition) == on_ac_power());
         condition_free(condition);
 }
 
@@ -52,22 +68,22 @@ static void test_condition_test_host(void) {
         assert_se(sd_id128_to_string(id, sid));
 
         condition = condition_new(CONDITION_HOST, sid, false, false);
-        assert_se(condition_test_host(condition));
+        assert_se(condition_test(condition));
         condition_free(condition);
 
         condition = condition_new(CONDITION_HOST, "garbage value jjjjjjjjjjjjjj", false, false);
-        assert_se(!condition_test_host(condition));
+        assert_se(!condition_test(condition));
         condition_free(condition);
 
         condition = condition_new(CONDITION_HOST, sid, false, true);
-        assert_se(!condition_test_host(condition));
+        assert_se(!condition_test(condition));
         condition_free(condition);
 
         hostname = gethostname_malloc();
         assert_se(hostname);
 
         condition = condition_new(CONDITION_HOST, hostname, false, false);
-        assert_se(condition_test_host(condition));
+        assert_se(condition_test(condition));
         condition_free(condition);
 }
 
@@ -83,15 +99,15 @@ static void test_condition_test_architecture(void) {
         assert_se(sa);
 
         condition = condition_new(CONDITION_ARCHITECTURE, sa, false, false);
-        assert_se(condition_test_architecture(condition));
+        assert_se(condition_test(condition));
         condition_free(condition);
 
         condition = condition_new(CONDITION_ARCHITECTURE, "garbage value", false, false);
-        assert_se(condition_test_architecture(condition) < 0);
+        assert_se(condition_test(condition) < 0);
         condition_free(condition);
 
         condition = condition_new(CONDITION_ARCHITECTURE, sa, false, true);
-        assert_se(!condition_test_architecture(condition));
+        assert_se(!condition_test(condition));
         condition_free(condition);
 }
 
@@ -99,11 +115,11 @@ static void test_condition_test_kernel_command_line(void) {
         Condition *condition;
 
         condition = condition_new(CONDITION_KERNEL_COMMAND_LINE, "thisreallyshouldntbeonthekernelcommandline", false, false);
-        assert_se(!condition_test_kernel_command_line(condition));
+        assert_se(!condition_test(condition));
         condition_free(condition);
 
         condition = condition_new(CONDITION_KERNEL_COMMAND_LINE, "andthis=neither", false, false);
-        assert_se(!condition_test_kernel_command_line(condition));
+        assert_se(!condition_test(condition));
         condition_free(condition);
 }
 
@@ -111,6 +127,7 @@ int main(int argc, char *argv[]) {
         log_parse_environment();
         log_open();
 
+        test_condition_test_path_exists();
         test_condition_test_ac_power();
         test_condition_test_host();
         test_condition_test_architecture();

commit 592fd144ae313855f48d0ca52a103013b41e5d59
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Nov 6 00:49:44 2014 +0100

    condition: properly allow passing back errors from condition checks

diff --git a/src/core/condition.c b/src/core/condition.c
index 8e2e311..32135ec 100644
--- a/src/core/condition.c
+++ b/src/core/condition.c
@@ -40,7 +40,7 @@
 #include "selinux-util.h"
 #include "audit.h"
 
-static bool condition_test_security(Condition *c) {
+static int condition_test_security(Condition *c) {
         assert(c);
         assert(c->parameter);
         assert(c->type == CONDITION_SECURITY);
@@ -59,7 +59,7 @@ static bool condition_test_security(Condition *c) {
         return c->negate;
 }
 
-static bool condition_test_capability(Condition *c) {
+static int condition_test_capability(Condition *c) {
         _cleanup_fclose_ FILE *f = NULL;
         cap_value_t value;
         char line[LINE_MAX];
@@ -72,14 +72,14 @@ static bool condition_test_capability(Condition *c) {
         /* If it's an invalid capability, we don't have it */
 
         if (cap_from_name(c->parameter, &value) < 0)
-                return c->negate;
+                return -EINVAL;
 
         /* If it's a valid capability we default to assume
          * that we have it */
 
         f = fopen("/proc/self/status", "re");
         if (!f)
-                return !c->negate;
+                return -errno;
 
         while (fgets(line, sizeof(line), f)) {
                 truncate_nl(line);
@@ -132,12 +132,12 @@ static bool condition_test_first_boot(Condition *c) {
 
         r = parse_boolean(c->parameter);
         if (r < 0)
-                return c->negate;
+                return r;
 
         return ((access("/run/systemd/first-boot", F_OK) >= 0) == !!r) == !c->negate;
 }
 
-static bool condition_test(Condition *c) {
+static int condition_test(Condition *c) {
         assert(c);
 
         switch(c->type) {
@@ -242,25 +242,35 @@ bool condition_test_list(const char *unit, Condition *first) {
          * if any of the trigger conditions apply (unless there are
          * none) we return true */
         LIST_FOREACH(conditions, c, first) {
-                bool b;
-
-                b = condition_test(c);
-                if (unit)
+                int r;
+
+                r = condition_test(c);
+                if (r < 0)
+                        log_warning_unit(unit,
+                                         "Couldn't determine result for %s=%s%s%s for %s, assuming failed: %s",
+                                         condition_type_to_string(c->type),
+                                         c->trigger ? "|" : "",
+                                         c->negate ? "!" : "",
+                                         c->parameter,
+                                         unit,
+                                         strerror(-r));
+                else
                         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",
+                                       r > 0 ? "succeeded" : "failed",
                                        unit);
-                c->state = b ? 1 : -1;
 
-                if (!c->trigger && !b)
+                c->state = r > 0 ? CONDITION_STATE_SUCCEEDED : CONDITION_STATE_FAILED;
+
+                if (!c->trigger && r <= 0)
                         return false;
 
                 if (c->trigger && triggered <= 0)
-                        triggered = b;
+                        triggered = r > 0;
         }
 
         return triggered != 0;
diff --git a/src/shared/architecture.c b/src/shared/architecture.c
index dc45f35..34c5a53 100644
--- a/src/shared/architecture.c
+++ b/src/shared/architecture.c
@@ -23,7 +23,7 @@
 
 #include "architecture.h"
 
-Architecture uname_architecture(void) {
+int uname_architecture(void) {
 
         /* Return a sanitized enum identifying the architecture we are
          * running on. This is based on uname(), and the user may
@@ -41,7 +41,7 @@ Architecture uname_architecture(void) {
 
         static const struct {
                 const char *machine;
-                Architecture arch;
+                int arch;
         } arch_map[] = {
 #if defined(__x86_64__) || defined(__i386__)
                 { "x86_64",     ARCHITECTURE_X86_64   },
@@ -121,7 +121,7 @@ Architecture uname_architecture(void) {
 #endif
         };
 
-        static Architecture cached = _ARCHITECTURE_INVALID;
+        static int cached = _ARCHITECTURE_INVALID;
         struct utsname u;
         unsigned i;
 
@@ -168,4 +168,4 @@ static const char *const architecture_table[_ARCHITECTURE_MAX] = {
         [ARCHITECTURE_CRIS] = "cris",
 };
 
-DEFINE_STRING_TABLE_LOOKUP(architecture, Architecture);
+DEFINE_STRING_TABLE_LOOKUP(architecture, int);
diff --git a/src/shared/architecture.h b/src/shared/architecture.h
index 353d49b..f1fef23 100644
--- a/src/shared/architecture.h
+++ b/src/shared/architecture.h
@@ -30,7 +30,7 @@
  * focus on general family, and distuignish word width and
  * endianness. */
 
-typedef enum Architecture {
+enum {
         ARCHITECTURE_X86 = 0,
         ARCHITECTURE_X86_64,
         ARCHITECTURE_PPC,
@@ -60,9 +60,9 @@ typedef enum Architecture {
         ARCHITECTURE_CRIS,
         _ARCHITECTURE_MAX,
         _ARCHITECTURE_INVALID = -1
-} Architecture;
+};
 
-Architecture uname_architecture(void);
+int uname_architecture(void);
 
 /*
  * LIB_ARCH_TUPLE should resolve to the local library path
@@ -188,5 +188,5 @@ Architecture uname_architecture(void);
 #error "Please register your architecture here!"
 #endif
 
-const char *architecture_to_string(Architecture a) _const_;
-Architecture architecture_from_string(const char *s) _pure_;
+const char *architecture_to_string(int a) _const_;
+int architecture_from_string(const char *s) _pure_;
diff --git a/src/shared/condition-util.c b/src/shared/condition-util.c
index 026b6a8..33752c8 100644
--- a/src/shared/condition-util.c
+++ b/src/shared/condition-util.c
@@ -73,7 +73,7 @@ void condition_free_list(Condition *first) {
                 condition_free(c);
 }
 
-bool condition_test_kernel_command_line(Condition *c) {
+int condition_test_kernel_command_line(Condition *c) {
         _cleanup_free_ char *line = NULL;
         const char *p;
         bool equal;
@@ -85,8 +85,8 @@ bool condition_test_kernel_command_line(Condition *c) {
 
         r = proc_cmdline(&line);
         if (r < 0)
-                log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
-        if (r <= 0)
+                return r;
+        if (r == 0)
                 return c->negate;
 
         equal = !!strchr(c->parameter, '=');
@@ -97,7 +97,9 @@ bool condition_test_kernel_command_line(Condition *c) {
                 bool found;
 
                 r = unquote_first_word(&p, &word);
-                if (r <= 0)
+                if (r < 0)
+                        return r;
+                if (r == 0)
                         return c->negate;
 
                 if (equal)
@@ -116,7 +118,7 @@ bool condition_test_kernel_command_line(Condition *c) {
         return c->negate;
 }
 
-bool condition_test_virtualization(Condition *c) {
+int condition_test_virtualization(Condition *c) {
         int b, v;
         const char *id;
 
@@ -125,10 +127,8 @@ bool condition_test_virtualization(Condition *c) {
         assert(c->type == CONDITION_VIRTUALIZATION);
 
         v = detect_virtualization(&id);
-        if (v < 0) {
-                log_warning("Failed to detect virtualization, ignoring: %s", strerror(-v));
-                return c->negate;
-        }
+        if (v < 0)
+                return v;
 
         /* First, compare with yes/no */
         b = parse_boolean(c->parameter);
@@ -150,8 +150,8 @@ bool condition_test_virtualization(Condition *c) {
         return (v > 0 && streq(c->parameter, id)) == !c->negate;
 }
 
-bool condition_test_architecture(Condition *c) {
-        Architecture a, b;
+int condition_test_architecture(Condition *c) {
+        int a, b;
 
         assert(c);
         assert(c->parameter);
@@ -159,20 +159,19 @@ bool condition_test_architecture(Condition *c) {
 
         a = uname_architecture();
         if (a < 0)
-                return c->negate;
+                return a;
 
         if (streq(c->parameter, "native"))
                 b = native_architecture();
         else
                 b = architecture_from_string(c->parameter);
-
         if (b < 0)
-                return c->negate;
+                return b;
 
         return (a == b) == !c->negate;
 }
 
-bool condition_test_host(Condition *c) {
+int condition_test_host(Condition *c) {
         _cleanup_free_ char *h = NULL;
         sd_id128_t x, y;
         int r;
@@ -185,19 +184,19 @@ bool condition_test_host(Condition *c) {
 
                 r = sd_id128_get_machine(&y);
                 if (r < 0)
-                        return c->negate;
+                        return r;
 
                 return sd_id128_equal(x, y) == !c->negate;
         }
 
         h = gethostname_malloc();
         if (!h)
-                return c->negate;
+                return -ENOMEM;
 
         return (fnmatch(c->parameter, h, FNM_CASEFOLD) == 0) == !c->negate;
 }
 
-bool condition_test_ac_power(Condition *c) {
+int condition_test_ac_power(Condition *c) {
         int r;
 
         assert(c);
@@ -206,7 +205,7 @@ bool condition_test_ac_power(Condition *c) {
 
         r = parse_boolean(c->parameter);
         if (r < 0)
-                return !c->negate;
+                return r;
 
         return ((on_ac_power() != 0) == !!r) == !c->negate;
 }
@@ -225,7 +224,7 @@ void condition_dump(Condition *c, FILE *f, const char *prefix) {
                 c->trigger ? "|" : "",
                 c->negate ? "!" : "",
                 c->parameter,
-                c->state < 0 ? "failed" : c->state > 0 ? "succeeded" : "untested");
+                CONDITION_STATE_IS_FAILED(c->state) ? "failed" : CONDITION_STATE_IS_SUCCEEDED(c->state) ? "succeeded" : "untested");
 }
 
 void condition_dump_list(Condition *first, FILE *f, const char *prefix) {
diff --git a/src/shared/condition-util.h b/src/shared/condition-util.h
index 047fdbf..fbb8150 100644
--- a/src/shared/condition-util.h
+++ b/src/shared/condition-util.h
@@ -51,6 +51,16 @@ typedef enum ConditionType {
         _CONDITION_TYPE_INVALID = -1
 } ConditionType;
 
+#define CONDITION_STATE_IS_SUCCEEDED(state) ((state) > 0)
+#define CONDITION_STATE_IS_UNKNOWN(state) ((state) == 0)
+#define CONDITION_STATE_IS_FAILED(state) ((state) < 0)
+
+enum {
+        CONDITION_STATE_SUCCEEDED = -1,
+        CONDITION_STATE_UNKNOWN = 0,
+        CONDITION_STATE_FAILED = 1
+};
+
 typedef struct Condition {
         ConditionType type;
 
@@ -58,7 +68,6 @@ typedef struct Condition {
         bool negate:1;
 
         char *parameter;
-
         int state;
 
         LIST_FIELDS(struct Condition, conditions);
@@ -68,11 +77,11 @@ Condition* condition_new(ConditionType type, const char *parameter, bool trigger
 void condition_free(Condition *c);
 void condition_free_list(Condition *c);
 
-bool condition_test_kernel_command_line(Condition *c);
-bool condition_test_virtualization(Condition *c);
-bool condition_test_architecture(Condition *c);
-bool condition_test_host(Condition *c);
-bool condition_test_ac_power(Condition *c);
+int condition_test_kernel_command_line(Condition *c);
+int condition_test_virtualization(Condition *c);
+int condition_test_architecture(Condition *c);
+int condition_test_host(Condition *c);
+int condition_test_ac_power(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/test/test-architecture.c b/src/test/test-architecture.c
index 24217ad..30bdec4 100644
--- a/src/test/test-architecture.c
+++ b/src/test/test-architecture.c
@@ -25,9 +25,8 @@
 #include "log.h"
 
 int main(int argc, char *argv[]) {
-        Architecture a;
-        int v;
         const char *id = NULL;
+        int a, v;
 
         v = detect_virtualization(&id);
         if (v == -EPERM || v == -EACCES)
diff --git a/src/test/test-condition-util.c b/src/test/test-condition-util.c
index 1c79244..7a247fb 100644
--- a/src/test/test-condition-util.c
+++ b/src/test/test-condition-util.c
@@ -73,8 +73,8 @@ static void test_condition_test_host(void) {
 
 static void test_condition_test_architecture(void) {
         Condition *condition;
-        Architecture a;
         const char *sa;
+        int a;
 
         a = uname_architecture();
         assert_se(a >= 0);
@@ -87,7 +87,7 @@ static void test_condition_test_architecture(void) {
         condition_free(condition);
 
         condition = condition_new(CONDITION_ARCHITECTURE, "garbage value", false, false);
-        assert_se(!condition_test_architecture(condition));
+        assert_se(condition_test_architecture(condition) < 0);
         condition_free(condition);
 
         condition = condition_new(CONDITION_ARCHITECTURE, sa, false, true);



More information about the systemd-commits mailing list