core: introduce the concept of AssertXYZ= similar to ConditionXYZ=, but fatal for a start job if not met
This commit is contained in:
parent
cc50ef134b
commit
59fccdc587
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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),
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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_;
|
||||
|
|
|
@ -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, ¶m, &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)
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue