From: Lennart Poettering Date: Thu, 6 Nov 2014 12:43:45 +0000 (+0100) Subject: core: introduce the concept of AssertXYZ= similar to ConditionXYZ=, but fatal for... X-Git-Tag: v218~531 X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=commitdiff_plain;h=59fccdc587bc179c1638916ee16a24099f94f81f core: introduce the concept of AssertXYZ= similar to ConditionXYZ=, but fatal for a start job if not met --- diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml index 6d4c5c11e..5d6f6eb14 100644 --- a/man/systemd.unit.xml +++ b/man/systemd.unit.xml @@ -1264,6 +1264,39 @@ have no effect. + + AssertArchitecture= + AssertVirtualization= + AssertHost= + AssertKernelCommandLine= + AssertSecurity= + AssertCapability= + AssertACPower= + AssertNeedsUpdate= + AssertFirstBoot= + AssertPathExists= + AssertPathExistsGlob= + AssertPathIsDirectory= + AssertPathIsSymbolicLink= + AssertPathIsMountPoint= + AssertPathIsReadWrite= + AssertDirectoryNotEmpty= + AssertFileNotEmpty= + AssertFileIsExecutable= + AssertNull= + + Similar to the + ConditionArchitecture=, + ConditionVirtualization=, + ... 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. + + SourcePath= A path to a diff --git a/src/core/condition.c b/src/core/condition.c index 847525867..c20c0f01e 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 6dd77bb65..a6a31edc7 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 3fa427198..5dcde25a2 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 eaa4bb17f..51d15811b 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 1e7c61b04..b7ebd8dc8 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 5158a9f15..1d2debe70 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 e193a67dc..2ee16bdef 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 d5acc728e..66f53ddc7 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 081ab18f1..8b2427224 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 640a931ff..ee9d11ee8 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 08aee94a8..9aaaee909 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 8481a9b20..8a3e203e9 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, ¶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) diff --git a/src/test/test-tables.c b/src/test/test-tables.c index 213844219..97d5609ad 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);