From c154bb65ad55a98fdf7d05fcb6b68a22408ea33a Mon Sep 17 00:00:00 2001 From: Matteo Croce Date: Thu, 20 Feb 2025 13:07:24 +0100 Subject: [PATCH] condition: introduce ConditionVersion=/AssertVersion= Add a new condition wich checks against systemd version. Change condition_test_kernel_version() into a generic condition_test_version() so most of the code can be reused. $ systemctl --version systemd 258 (258~devel-g53ca5f6) $ systemd-analyze condition 'ConditionVersion=systemd>255' test.service: ConditionVersion=>255 succeeded. $ systemd-analyze condition 'ConditionVersion=systemd>260' test.service: ConditionVersion=>260 failed. $ systemd-analyze condition 'ConditionVersion=systemd>=258' test.service: ConditionVersion=>=258 succeeded. $ systemd-analyze condition 'ConditionVersion=systemd>=257.1' test.service: ConditionVersion=>=257.1 succeeded. $ uname -r 6.12.13-200.fc41.aarch64 $ systemd-analyze condition 'ConditionVersion=kernel > 4.4' test.service: ConditionVersion=kernel > 4.4 succeeded. $ systemd-analyze condition 'ConditionVersion=kernel > 6.20' test.service: ConditionVersion=kernel > 6.20 failed. $ systemd-analyze condition 'ConditionVersion=kernel < 9.0' test.service: ConditionVersion=kernel < 9.0 succeeded. --- docs/TRANSIENT-SETTINGS.md | 1 + man/systemd-analyze.xml | 8 +- man/systemd.link.xml | 13 ++ man/systemd.netdev.xml | 1 + man/systemd.network.xml | 1 + man/systemd.unit.xml | 18 +++ src/core/load-fragment-gperf.gperf.in | 6 +- src/network/netdev/netdev-gperf.gperf | 3 +- src/network/networkd-network-gperf.gperf | 3 +- src/shared/condition.c | 83 +++++++++--- src/shared/condition.h | 2 +- src/test/test-condition.c | 153 +++++++++++++++++++---- src/udev/net/link-config-gperf.gperf | 3 +- test/test-execute/exec-basic.service | 15 +++ 14 files changed, 260 insertions(+), 50 deletions(-) diff --git a/docs/TRANSIENT-SETTINGS.md b/docs/TRANSIENT-SETTINGS.md index ebb8ba536a3..4992acbf673 100644 --- a/docs/TRANSIENT-SETTINGS.md +++ b/docs/TRANSIENT-SETTINGS.md @@ -69,6 +69,7 @@ Most generic unit settings are available for transient units. ✓ ConditionFirstBoot= ✓ ConditionKernelCommandLine= ✓ ConditionKernelVersion= +✓ ConditionVersion= ✓ ConditionArchitecture= ✓ ConditionFirmware= ✓ ConditionVirtualization= diff --git a/man/systemd-analyze.xml b/man/systemd-analyze.xml index a1250eb10ff..b3975d5f03e 100644 --- a/man/systemd-analyze.xml +++ b/man/systemd-analyze.xml @@ -525,8 +525,8 @@ cap_net_raw 13 Evaluate conditions that check kernel versions - $ systemd-analyze condition 'ConditionKernelVersion = ! <4.0' \ - 'ConditionKernelVersion = >=5.1' \ + $ systemd-analyze condition 'ConditionVersion = ! <4.0' \ + 'ConditionVersion = >=5.1' \ 'ConditionACPower=|false' \ 'ConditionArchitecture=|!arm' \ 'AssertPathExists=/etc/os-release' @@ -534,8 +534,8 @@ test.service: AssertPathExists=/etc/os-release succeeded. Asserts succeeded. test.service: ConditionArchitecture=|!arm succeeded. test.service: ConditionACPower=|false failed. -test.service: ConditionKernelVersion=>=5.1 succeeded. -test.service: ConditionKernelVersion=!<4.0 succeeded. +test.service: ConditionVersion=>=5.1 succeeded. +test.service: ConditionVersion=!<4.0 succeeded. Conditions succeeded. diff --git a/man/systemd.link.xml b/man/systemd.link.xml index 5441da377e5..217352f9797 100644 --- a/man/systemd.link.xml +++ b/man/systemd.link.xml @@ -306,6 +306,19 @@ + + Version= + + Checks whether a software version matches a certain expression. See + ConditionVersion= in + systemd.unit5 + for details. When prefixed with an exclamation mark (!), the result is negated. + If an empty string is assigned, the previously assigned value is cleared. + + + + + Credential= diff --git a/man/systemd.netdev.xml b/man/systemd.netdev.xml index b88c51b90e7..1b4673ab804 100644 --- a/man/systemd.netdev.xml +++ b/man/systemd.netdev.xml @@ -223,6 +223,7 @@ + diff --git a/man/systemd.network.xml b/man/systemd.network.xml index c1618c0f94c..52194c4fc0c 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -152,6 +152,7 @@ + diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml index e931ef6f76b..5be9225a587 100644 --- a/man/systemd.unit.xml +++ b/man/systemd.unit.xml @@ -1481,6 +1481,24 @@ + + ConditionVersion= + + ConditionVersion= may be used to check whether a software + version matches a certain expression, or if prefixed with the exclamation mark, does not match. + The first argument is the software whose version has to be checked. Currently + kernel and systemd are supported. If this argument is + omitted, kernel is implied. The second argument must be a list + of (potentially quoted) expressions. Each expression starts with one of = or + != for string comparisons, <, <=, + ==, <>, >=, + > for version comparisons, or $=, !$= + for a shell-style glob match. If no operator is specified, $= is implied. + + + + + ConditionCredential= diff --git a/src/core/load-fragment-gperf.gperf.in b/src/core/load-fragment-gperf.gperf.in index 7344e56c4a2..6307dbe83f1 100644 --- a/src/core/load-fragment-gperf.gperf.in +++ b/src/core/load-fragment-gperf.gperf.in @@ -360,7 +360,8 @@ Unit.ConditionFirmware, config_parse_unit_condition_string Unit.ConditionVirtualization, config_parse_unit_condition_string, CONDITION_VIRTUALIZATION, offsetof(Unit, conditions) Unit.ConditionHost, config_parse_unit_condition_string, CONDITION_HOST, offsetof(Unit, conditions) Unit.ConditionKernelCommandLine, config_parse_unit_condition_string, CONDITION_KERNEL_COMMAND_LINE, offsetof(Unit, conditions) -Unit.ConditionKernelVersion, config_parse_unit_condition_string, CONDITION_KERNEL_VERSION, offsetof(Unit, conditions) +Unit.ConditionKernelVersion, config_parse_unit_condition_string, CONDITION_VERSION, offsetof(Unit, conditions) +Unit.ConditionVersion, config_parse_unit_condition_string, CONDITION_VERSION, offsetof(Unit, conditions) Unit.ConditionCredential, config_parse_unit_condition_string, CONDITION_CREDENTIAL, 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) @@ -393,7 +394,8 @@ Unit.AssertArchitecture, config_parse_unit_condition_string Unit.AssertVirtualization, config_parse_unit_condition_string, CONDITION_VIRTUALIZATION, offsetof(Unit, asserts) Unit.AssertHost, config_parse_unit_condition_string, CONDITION_HOST, offsetof(Unit, asserts) Unit.AssertKernelCommandLine, config_parse_unit_condition_string, CONDITION_KERNEL_COMMAND_LINE, offsetof(Unit, asserts) -Unit.AssertKernelVersion, config_parse_unit_condition_string, CONDITION_KERNEL_VERSION, offsetof(Unit, asserts) +Unit.AssertKernelVersion, config_parse_unit_condition_string, CONDITION_VERSION, offsetof(Unit, asserts) +Unit.AssertVersion, config_parse_unit_condition_string, CONDITION_VERSION, offsetof(Unit, asserts) Unit.AssertCredential, config_parse_unit_condition_string, CONDITION_CREDENTIAL, 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) diff --git a/src/network/netdev/netdev-gperf.gperf b/src/network/netdev/netdev-gperf.gperf index 612f463e337..4535c2cf3e0 100644 --- a/src/network/netdev/netdev-gperf.gperf +++ b/src/network/netdev/netdev-gperf.gperf @@ -45,7 +45,8 @@ struct ConfigPerfItem; Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(NetDev, conditions) Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(NetDev, conditions) Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(NetDev, conditions) -Match.KernelVersion, config_parse_net_condition, CONDITION_KERNEL_VERSION, offsetof(NetDev, conditions) +Match.KernelVersion, config_parse_net_condition, CONDITION_VERSION, offsetof(NetDev, conditions) +Match.Version, config_parse_net_condition, CONDITION_VERSION, offsetof(NetDev, conditions) Match.Credential, config_parse_net_condition, CONDITION_CREDENTIAL, offsetof(NetDev, conditions) Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(NetDev, conditions) Match.Firmware, config_parse_net_condition, CONDITION_FIRMWARE, offsetof(NetDev, conditions) diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index bdbb3ad2c8a..8e99a059c1b 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -67,7 +67,8 @@ Match.Property, config_parse_match_property, Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(Network, conditions) Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(Network, conditions) Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(Network, conditions) -Match.KernelVersion, config_parse_net_condition, CONDITION_KERNEL_VERSION, offsetof(Network, conditions) +Match.KernelVersion, config_parse_net_condition, CONDITION_VERSION, offsetof(Network, conditions) +Match.Version, config_parse_net_condition, CONDITION_VERSION, offsetof(Network, conditions) Match.Credential, config_parse_net_condition, CONDITION_CREDENTIAL, offsetof(Network, conditions) Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(Network, conditions) Match.Firmware, config_parse_net_condition, CONDITION_FIRMWARE, offsetof(Network, conditions) diff --git a/src/shared/condition.c b/src/shared/condition.c index 983bda7d7b3..b65d64d300d 100644 --- a/src/shared/condition.c +++ b/src/shared/condition.c @@ -185,18 +185,14 @@ static int condition_test_credential(Condition *c, char **env) { return false; } -static int condition_test_kernel_version(Condition *c, char **env) { +static int condition_test_version_cmp(const char *condition, const char *ver) { CompareOperator operator; - struct utsname u; bool first = true; - assert(c); - assert(c->parameter); - assert(c->type == CONDITION_KERNEL_VERSION); - - assert_se(uname(&u) >= 0); + assert(condition); + assert(ver); - for (const char *p = c->parameter;;) { + for (const char *p = condition;;) { _cleanup_free_ char *word = NULL; const char *s; int r; @@ -228,7 +224,7 @@ static int condition_test_kernel_version(Condition *c, char **env) { return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Unexpected end of expression: %s", p); } - r = version_or_fnmatch_compare(operator, u.release, s); + r = version_or_fnmatch_compare(operator, ver, s); if (r < 0) return r; if (!r) @@ -240,6 +236,37 @@ static int condition_test_kernel_version(Condition *c, char **env) { return true; } +static int condition_test_version(Condition *c, char **env) { + int r; + + assert(c); + assert(c->type == CONDITION_VERSION); + + /* An empty condition is considered true. */ + if (isempty(c->parameter)) + return true; + + const char *p = c->parameter; + _cleanup_free_ char *word = NULL; + r = extract_first_word(&p, &word, COMPARE_OPERATOR_WITH_FNMATCH_CHARS WHITESPACE, + EXTRACT_DONT_COALESCE_SEPARATORS|EXTRACT_RETAIN_SEPARATORS); + if (r < 0) + return log_debug_errno(r, "Failed to parse compare predicate \"%s\": %m", p); + if (r == 0) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Missing right operand in condition: %s", c->parameter); + + if (streq(word, "systemd")) + return condition_test_version_cmp(p, STRINGIFY(PROJECT_VERSION)); + + /* if no predicate has been set, default to "kernel" and use the whole parameter as condition */ + if (!streq(word, "kernel")) + p = c->parameter; + + struct utsname u; + assert_se(uname(&u) >= 0); + return condition_test_version_cmp(p, u.release); +} + static int condition_test_osrelease(Condition *c, char **env) { int r; @@ -1222,7 +1249,7 @@ int condition_test(Condition *c, char **env) { [CONDITION_FILE_NOT_EMPTY] = condition_test_file_not_empty, [CONDITION_FILE_IS_EXECUTABLE] = condition_test_file_is_executable, [CONDITION_KERNEL_COMMAND_LINE] = condition_test_kernel_command_line, - [CONDITION_KERNEL_VERSION] = condition_test_kernel_version, + [CONDITION_VERSION] = condition_test_version, [CONDITION_CREDENTIAL] = condition_test_credential, [CONDITION_VIRTUALIZATION] = condition_test_virtualization, [CONDITION_SECURITY] = condition_test_security, @@ -1335,13 +1362,13 @@ void condition_dump_list(Condition *first, FILE *f, const char *prefix, conditio condition_dump(c, f, prefix, to_string); } -static const char* const condition_type_table[_CONDITION_TYPE_MAX] = { +static const char* const _condition_type_table[_CONDITION_TYPE_MAX] = { [CONDITION_ARCHITECTURE] = "ConditionArchitecture", [CONDITION_FIRMWARE] = "ConditionFirmware", [CONDITION_VIRTUALIZATION] = "ConditionVirtualization", [CONDITION_HOST] = "ConditionHost", [CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine", - [CONDITION_KERNEL_VERSION] = "ConditionKernelVersion", + [CONDITION_VERSION] = "ConditionVersion", [CONDITION_CREDENTIAL] = "ConditionCredential", [CONDITION_SECURITY] = "ConditionSecurity", [CONDITION_CAPABILITY] = "ConditionCapability", @@ -1372,15 +1399,27 @@ static const char* const condition_type_table[_CONDITION_TYPE_MAX] = { [CONDITION_KERNEL_MODULE_LOADED] = "ConditionKernelModuleLoaded", }; -DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType); +DEFINE_PRIVATE_STRING_TABLE_LOOKUP(_condition_type, ConditionType); + +const char* condition_type_to_string(ConditionType t) { + return _condition_type_to_string(t); +} + +ConditionType condition_type_from_string(const char *s) { + /* for backward compatibility */ + if (streq_ptr(s, "ConditionKernelVersion")) + return CONDITION_VERSION; -static const char* const assert_type_table[_CONDITION_TYPE_MAX] = { + return _condition_type_from_string(s); +} + +static const char* const _assert_type_table[_CONDITION_TYPE_MAX] = { [CONDITION_ARCHITECTURE] = "AssertArchitecture", [CONDITION_FIRMWARE] = "AssertFirmware", [CONDITION_VIRTUALIZATION] = "AssertVirtualization", [CONDITION_HOST] = "AssertHost", [CONDITION_KERNEL_COMMAND_LINE] = "AssertKernelCommandLine", - [CONDITION_KERNEL_VERSION] = "AssertKernelVersion", + [CONDITION_VERSION] = "AssertVersion", [CONDITION_CREDENTIAL] = "AssertCredential", [CONDITION_SECURITY] = "AssertSecurity", [CONDITION_CAPABILITY] = "AssertCapability", @@ -1411,7 +1450,19 @@ static const char* const assert_type_table[_CONDITION_TYPE_MAX] = { [CONDITION_KERNEL_MODULE_LOADED] = "AssertKernelModuleLoaded", }; -DEFINE_STRING_TABLE_LOOKUP(assert_type, ConditionType); +DEFINE_PRIVATE_STRING_TABLE_LOOKUP(_assert_type, ConditionType); + +const char* assert_type_to_string(ConditionType t) { + return _assert_type_to_string(t); +} + +ConditionType assert_type_from_string(const char *s) { + /* for backward compatibility */ + if (streq_ptr(s, "AssertKernelVersion")) + return CONDITION_VERSION; + + return _assert_type_from_string(s); +} static const char* const condition_result_table[_CONDITION_RESULT_MAX] = { [CONDITION_UNTESTED] = "untested", diff --git a/src/shared/condition.h b/src/shared/condition.h index 378028a73e2..84b92738414 100644 --- a/src/shared/condition.h +++ b/src/shared/condition.h @@ -13,7 +13,7 @@ typedef enum ConditionType { CONDITION_VIRTUALIZATION, CONDITION_HOST, CONDITION_KERNEL_COMMAND_LINE, - CONDITION_KERNEL_VERSION, + CONDITION_VERSION, CONDITION_CREDENTIAL, CONDITION_SECURITY, CONDITION_CAPABILITY, diff --git a/src/test/test-condition.c b/src/test/test-condition.c index 9334a8ca89a..a6132f1132b 100644 --- a/src/test/test-condition.c +++ b/src/test/test-condition.c @@ -460,115 +460,220 @@ TEST(condition_test_kernel_version) { struct utsname u; const char *v; - ASSERT_NOT_NULL((condition = condition_new(CONDITION_KERNEL_VERSION, "*thisreallyshouldntbeinthekernelversion*", false, false))); + ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, "*thisreallyshouldntbeinthekernelversion*", false, false))); ASSERT_OK_ZERO(condition_test(condition, environ)); condition_free(condition); - ASSERT_NOT_NULL((condition = condition_new(CONDITION_KERNEL_VERSION, "*", false, false))); + ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, "*", false, false))); ASSERT_OK_POSITIVE(condition_test(condition, environ)); condition_free(condition); /* An artificially empty condition. It evaluates to true, but normally * such condition cannot be created, because the condition list is reset instead. */ - ASSERT_NOT_NULL((condition = condition_new(CONDITION_KERNEL_VERSION, "", false, false))); + ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, "", false, false))); ASSERT_OK_POSITIVE(condition_test(condition, environ)); condition_free(condition); ASSERT_OK_ERRNO(uname(&u)); - ASSERT_NOT_NULL((condition = condition_new(CONDITION_KERNEL_VERSION, u.release, false, false))); + ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, u.release, false, false))); ASSERT_OK_POSITIVE(condition_test(condition, environ)); condition_free(condition); strshorten(u.release, 4); strcpy(strchr(u.release, 0), "*"); - ASSERT_NOT_NULL((condition = condition_new(CONDITION_KERNEL_VERSION, u.release, false, false))); + ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, u.release, false, false))); ASSERT_OK_POSITIVE(condition_test(condition, environ)); condition_free(condition); /* 0.1.2 would be a very very very old kernel */ - ASSERT_NOT_NULL((condition = condition_new(CONDITION_KERNEL_VERSION, "> 0.1.2", false, false))); + ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, "> 0.1.2", false, false))); ASSERT_OK_POSITIVE(condition_test(condition, environ)); condition_free(condition); - ASSERT_NOT_NULL((condition = condition_new(CONDITION_KERNEL_VERSION, ">0.1.2", false, false))); + ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, ">0.1.2", false, false))); ASSERT_OK_POSITIVE(condition_test(condition, environ)); condition_free(condition); - ASSERT_NOT_NULL((condition = condition_new(CONDITION_KERNEL_VERSION, "'>0.1.2' '<9.0.0'", false, false))); + ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, "'>0.1.2' '<9.0.0'", false, false))); ASSERT_OK_POSITIVE(condition_test(condition, environ)); condition_free(condition); - ASSERT_NOT_NULL((condition = condition_new(CONDITION_KERNEL_VERSION, "> 0.1.2 < 9.0.0", false, false))); + ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, "> 0.1.2 < 9.0.0", false, false))); ASSERT_ERROR(condition_test(condition, environ), EINVAL); condition_free(condition); - ASSERT_NOT_NULL((condition = condition_new(CONDITION_KERNEL_VERSION, ">", false, false))); + ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, ">", false, false))); ASSERT_ERROR(condition_test(condition, environ), EINVAL); condition_free(condition); - ASSERT_NOT_NULL((condition = condition_new(CONDITION_KERNEL_VERSION, ">= 0.1.2", false, false))); + ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, ">= 0.1.2", false, false))); ASSERT_OK_POSITIVE(condition_test(condition, environ)); condition_free(condition); - ASSERT_NOT_NULL((condition = condition_new(CONDITION_KERNEL_VERSION, "< 0.1.2", false, false))); + ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, "< 0.1.2", false, false))); ASSERT_OK_ZERO(condition_test(condition, environ)); condition_free(condition); - ASSERT_NOT_NULL((condition = condition_new(CONDITION_KERNEL_VERSION, "<= 0.1.2", false, false))); + ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, "<= 0.1.2", false, false))); ASSERT_OK_ZERO(condition_test(condition, environ)); condition_free(condition); - ASSERT_NOT_NULL((condition = condition_new(CONDITION_KERNEL_VERSION, "= 0.1.2", false, false))); + ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, "= 0.1.2", false, false))); ASSERT_OK_ZERO(condition_test(condition, environ)); condition_free(condition); /* 4711.8.15 is a very very very future kernel */ - ASSERT_NOT_NULL((condition = condition_new(CONDITION_KERNEL_VERSION, "< 4711.8.15", false, false))); + ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, "< 4711.8.15", false, false))); ASSERT_OK_POSITIVE(condition_test(condition, environ)); condition_free(condition); - ASSERT_NOT_NULL((condition = condition_new(CONDITION_KERNEL_VERSION, "<= 4711.8.15", false, false))); + ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, "<= 4711.8.15", false, false))); ASSERT_OK_POSITIVE(condition_test(condition, environ)); condition_free(condition); - ASSERT_NOT_NULL((condition = condition_new(CONDITION_KERNEL_VERSION, "= 4711.8.15", false, false))); + ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, "= 4711.8.15", false, false))); ASSERT_OK_ZERO(condition_test(condition, environ)); condition_free(condition); - ASSERT_NOT_NULL((condition = condition_new(CONDITION_KERNEL_VERSION, "> 4711.8.15", false, false))); + ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, "> 4711.8.15", false, false))); ASSERT_OK_ZERO(condition_test(condition, environ)); condition_free(condition); - ASSERT_NOT_NULL((condition = condition_new(CONDITION_KERNEL_VERSION, " >= 4711.8.15", false, false))); + ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, " >= 4711.8.15", false, false))); ASSERT_OK_ZERO(condition_test(condition, environ)); condition_free(condition); ASSERT_OK_ERRNO(uname(&u)); v = strjoina(">=", u.release); - ASSERT_NOT_NULL((condition = condition_new(CONDITION_KERNEL_VERSION, v, false, false))); + ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, v, false, false))); ASSERT_OK_POSITIVE(condition_test(condition, environ)); condition_free(condition); v = strjoina("= ", u.release); - ASSERT_NOT_NULL((condition = condition_new(CONDITION_KERNEL_VERSION, v, false, false))); + ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, v, false, false))); ASSERT_OK_POSITIVE(condition_test(condition, environ)); condition_free(condition); v = strjoina("<=", u.release); - ASSERT_NOT_NULL((condition = condition_new(CONDITION_KERNEL_VERSION, v, false, false))); + ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, v, false, false))); ASSERT_OK_POSITIVE(condition_test(condition, environ)); condition_free(condition); v = strjoina("> ", u.release); - ASSERT_NOT_NULL((condition = condition_new(CONDITION_KERNEL_VERSION, v, false, false))); + ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, v, false, false))); ASSERT_OK_ZERO(condition_test(condition, environ)); condition_free(condition); v = strjoina("< ", u.release); - ASSERT_NOT_NULL((condition = condition_new(CONDITION_KERNEL_VERSION, v, false, false))); + ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, v, false, false))); + ASSERT_OK_ZERO(condition_test(condition, environ)); + condition_free(condition); +} + +TEST(condition_test_version) { + Condition *condition; + const char *v; + char ver[8]; + + ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, "systemd *thisreallyshouldntbeinthesystemdversion*", false, false))); + ASSERT_OK_ZERO(condition_test(condition, environ)); + condition_free(condition); + + ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, "systemd *", false, false))); + ASSERT_OK_POSITIVE(condition_test(condition, environ)); + condition_free(condition); + + /* An artificially empty condition. It evaluates to true, but normally + * such condition cannot be created, because the condition list is reset instead. */ + ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, "", false, false))); + ASSERT_OK_POSITIVE(condition_test(condition, environ)); + condition_free(condition); + + /* 42 would be a very very very old systemd release */ + ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, "systemd > 42", false, false))); + ASSERT_OK_POSITIVE(condition_test(condition, environ)); + condition_free(condition); + + ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, "systemd>42", false, false))); + ASSERT_OK_POSITIVE(condition_test(condition, environ)); + condition_free(condition); + + ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, "systemd '>42' '<9000'", false, false))); + ASSERT_OK_POSITIVE(condition_test(condition, environ)); + condition_free(condition); + + ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, "systemd > 42 < 9000", false, false))); + ASSERT_ERROR(condition_test(condition, environ), EINVAL); + condition_free(condition); + + ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, "systemd>", false, false))); + ASSERT_ERROR(condition_test(condition, environ), EINVAL); + condition_free(condition); + + ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, "systemd >= 42", false, false))); + ASSERT_OK_POSITIVE(condition_test(condition, environ)); + condition_free(condition); + + ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, "systemd < 42", false, false))); + ASSERT_OK_ZERO(condition_test(condition, environ)); + condition_free(condition); + + ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, "systemd <= 42", false, false))); + ASSERT_OK_ZERO(condition_test(condition, environ)); + condition_free(condition); + + ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, "systemd = 42", false, false))); + ASSERT_OK_ZERO(condition_test(condition, environ)); + condition_free(condition); + + /* 9000 is a very very very future systemd release */ + ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, "systemd < 9000", false, false))); + ASSERT_OK_POSITIVE(condition_test(condition, environ)); + condition_free(condition); + + ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, "systemd <= 9000", false, false))); + ASSERT_OK_POSITIVE(condition_test(condition, environ)); + condition_free(condition); + + ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, "systemd = 9000", false, false))); + ASSERT_OK_ZERO(condition_test(condition, environ)); + condition_free(condition); + + ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, "systemd > 9000", false, false))); + ASSERT_OK_ZERO(condition_test(condition, environ)); + condition_free(condition); + + ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, "systemd >= 9000", false, false))); + ASSERT_OK_ZERO(condition_test(condition, environ)); + condition_free(condition); + + xsprintf(ver, "%d", PROJECT_VERSION); + + v = strjoina("systemd>=", ver); + ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, v, false, false))); + ASSERT_OK_POSITIVE(condition_test(condition, environ)); + condition_free(condition); + + v = strjoina("systemd = ", ver); + ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, v, false, false))); + ASSERT_OK_POSITIVE(condition_test(condition, environ)); + condition_free(condition); + + v = strjoina("systemd<=", ver); + ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, v, false, false))); + ASSERT_OK_POSITIVE(condition_test(condition, environ)); + condition_free(condition); + + v = strjoina("systemd > ", ver); + ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, v, false, false))); + ASSERT_OK_ZERO(condition_test(condition, environ)); + condition_free(condition); + + v = strjoina("systemd < ", ver); + ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, v, false, false))); ASSERT_OK_ZERO(condition_test(condition, environ)); condition_free(condition); } diff --git a/src/udev/net/link-config-gperf.gperf b/src/udev/net/link-config-gperf.gperf index 4f4a016a425..d7cb07ba00f 100644 --- a/src/udev/net/link-config-gperf.gperf +++ b/src/udev/net/link-config-gperf.gperf @@ -34,7 +34,8 @@ Match.Property, config_parse_match_property, Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(LinkConfig, conditions) Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(LinkConfig, conditions) Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(LinkConfig, conditions) -Match.KernelVersion, config_parse_net_condition, CONDITION_KERNEL_VERSION, offsetof(LinkConfig, conditions) +Match.KernelVersion, config_parse_net_condition, CONDITION_VERSION, offsetof(LinkConfig, conditions) +Match.Version, config_parse_net_condition, CONDITION_VERSION, offsetof(LinkConfig, conditions) Match.Credential, config_parse_net_condition, CONDITION_CREDENTIAL, offsetof(LinkConfig, conditions) Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(LinkConfig, conditions) Match.Firmware, config_parse_net_condition, CONDITION_FIRMWARE, offsetof(LinkConfig, conditions) diff --git a/test/test-execute/exec-basic.service b/test/test-execute/exec-basic.service index a54aca9dc39..1fb300dc152 100644 --- a/test/test-execute/exec-basic.service +++ b/test/test-execute/exec-basic.service @@ -6,6 +6,21 @@ ConditionKernelVersion=">=2.0" "<=60" "!=1.4" ConditionKernelVersion=" >= 2.0" " <= 60 " "!= 1.4" ConditionKernelVersion=" >= 2.0" " * " "*.*" +ConditionVersion=kernel ">=3.0" +ConditionVersion=kernel ">=2.0" "<=60" "!=1.4" +ConditionVersion=kernel " >= 2.0" " <= 60 " "!= 1.4" +ConditionVersion=kernel " >= 2.0" " * " "*.*" + +ConditionVersion=">=3.0" +ConditionVersion=">=2.0" "<=60" "!=1.4" +ConditionVersion=" >= 2.0" " <= 60 " "!= 1.4" +ConditionVersion=" >= 2.0" " * " "*.*" + +ConditionVersion=systemd ">=30" +ConditionVersion=systemd ">=20" "<=9000" "!=14" +ConditionVersion=systemd " >= 20" " <= 9000 " "!= 14" +ConditionVersion=systemd " >= 20" " * " + [Service] ExecStart=touch /tmp/a ; /bin/sh -c 'touch /tmp/b' ; touch /tmp/c ExecStart=test -f /tmp/a -- 2.47.3