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

This commit is contained in:
Lennart Poettering 2014-11-06 13:43:45 +01:00
parent cc50ef134b
commit 59fccdc587
14 changed files with 312 additions and 122 deletions

View file

@ -1264,6 +1264,39 @@
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

View 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,

View 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));

View 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),

View 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);

View 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
};

View 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)

View 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 (!path_is_absolute(p)) {
log_syntax(unit, LOG_ERR, filename, line, EINVAL,
"Path in condition not absolute, ignoring: %s", p);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve specifiers, ignoring: %s", rvalue);
return 0;
}
c = condition_new(cond, p, trigger, negate);
if (!path_is_absolute(p)) {
log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Path in condition not absolute, ignoring: %s", p);
return 0;
}
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;
}

View 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;

View 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;

View 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",

View 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_;

View 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)

View 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);