summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--man/systemd.unit.xml33
-rw-r--r--src/core/condition.c6
-rw-r--r--src/core/condition.h2
-rw-r--r--src/core/dbus-unit.c17
-rw-r--r--src/core/job.c8
-rw-r--r--src/core/job.h1
-rw-r--r--src/core/load-fragment-gperf.gperf.m457
-rw-r--r--src/core/load-fragment.c131
-rw-r--r--src/core/unit.c53
-rw-r--r--src/core/unit.h3
-rw-r--r--src/shared/condition-util.c32
-rw-r--r--src/shared/condition-util.h22
-rw-r--r--src/systemctl/systemctl.c61
-rw-r--r--src/test/test-tables.c2
14 files changed, 309 insertions, 119 deletions
diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml
index 6d4c5c11eb..5d6f6eb141 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 8475258679..c20c0f01e1 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 6dd77bb658..a6a31edc7a 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 3fa427198e..5dcde25a2d 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 eaa4bb17fc..51d15811b5 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 1e7c61b04f..b7ebd8dc88 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 5158a9f158..1d2debe70f 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 e193a67dcd..2ee16bdef9 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 d5acc728ec..66f53ddc7c 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 081ab18f10..8b24272245 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 640a931ff5..ee9d11ee87 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 08aee94a89..9aaaee9091 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 8481a9b20c..8a3e203e9e 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 213844219e..97d5609adf 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);