chiark / gitweb /
core: introduce the concept of AssertXYZ= similar to ConditionXYZ=, but fatal for...
authorLennart Poettering <lennart@poettering.net>
Thu, 6 Nov 2014 12:43:45 +0000 (13:43 +0100)
committerLennart Poettering <lennart@poettering.net>
Thu, 6 Nov 2014 13:21:11 +0000 (14:21 +0100)
14 files changed:
man/systemd.unit.xml
src/core/condition.c
src/core/condition.h
src/core/dbus-unit.c
src/core/job.c
src/core/job.h
src/core/load-fragment-gperf.gperf.m4
src/core/load-fragment.c
src/core/unit.c
src/core/unit.h
src/shared/condition-util.c
src/shared/condition-util.h
src/systemctl/systemctl.c
src/test/test-tables.c

index 6d4c5c11eb6fab3bb4832a512e60063f4a2e5cb2..5d6f6eb1416ecbf2b8c619d29ff9975090e6691e 100644 (file)
                                 have no effect.</para></listitem>
                         </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
index 84752586796db20c2c20740a0705b7b47bc61873..c20c0f01e1da6df5ae90ad3b0b6e6872afc1626c 100644 (file)
@@ -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,
index 6dd77bb658323f7835441bfc6c606633bf325a0d..a6a31edc7ae9d58c9ead46b1aa5851714e8db82d 100644 (file)
@@ -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));
index 3fa427198e2016f9b8bed19b95814bc27f2e8395..5dcde25a2d5d1cf5f13d41765f579476f39530f6 100644 (file)
@@ -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),
 
index eaa4bb17fc1d42787613e9cb4269baeee77d209c..51d15811b51cc621d35438572e11773b4ca10812 100644 (file)
@@ -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);
index 1e7c61b04f30f54aff0c79549877ae06136509a7..b7ebd8dc88866b21855fb51994c2437565e96999 100644 (file)
@@ -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
 };
index 5158a9f158e034cd268978e7869d48d9ca4e70bb..1d2debe70f2cb24e1f53dfffac56c47bdd824608 100644 (file)
@@ -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)
index e193a67dcd202150904b448761d5f18644f854e9..2ee16bdef9e005782df4b2b729e79eef33d3afa3 100644 (file)
@@ -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;
 }
 
index d5acc728ecdec2527dd50d94dc487947abc0a2cd..66f53ddc7c61012c9a59dbdb2206f964f14766b4 100644 (file)
@@ -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;
 
index 081ab18f10289fd2b74dc2e9dc97e8ed959ddfcb..8b242722454a14bf63bc5c00f56358af100c6a85 100644 (file)
@@ -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;
index 640a931ff5b45417b8ed7a5044e08394dbe69c81..ee9d11ee8731b488b36217923422c7380babd945 100644 (file)
@@ -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",
index 08aee94a892d6b05d5d52c0c6125ebe356789bcd..9aaaee90916711d4b5147cafab11312a3c4d8deb 100644 (file)
@@ -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_;
index 8481a9b20cb3b52ab489a8c652b1e4ea66abe6a5..8a3e203e9e55261f81121c790fc64b143fb47359 100644 (file)
@@ -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)
index 213844219e2313ce6784184d0b932be702d00686..97d5609adffeb58a6a44aab22a2489a3963b89f2 100644 (file)
@@ -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);