mirror of
https://github.com/systemd/systemd.git
synced 2024-11-26 19:53:45 +08:00
core: add ConditionOSRelease= directive
This commit is contained in:
parent
70b6ee6110
commit
1e26f8a60b
@ -1646,6 +1646,19 @@
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>ConditionOSRelease=</varname></term>
|
||||
|
||||
<listitem><para>Verify that a specific <literal>key=value</literal> pair is set in the host's
|
||||
<citerefentry><refentrytitle>os-release</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
|
||||
|
||||
<para>Other than exact matching with <literal>=</literal>, and <literal>!=</literal>, relative
|
||||
comparisons are supported for versioned parameters (e.g. <literal>VERSION_ID</literal>). The
|
||||
comparator can be one of <literal><</literal>, <literal><=</literal>, <literal>=</literal>,
|
||||
<literal>!=</literal>, <literal>>=</literal> and <literal>></literal>.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>AssertArchitecture=</varname></term>
|
||||
<term><varname>AssertVirtualization=</varname></term>
|
||||
@ -1673,6 +1686,7 @@
|
||||
<term><varname>AssertControlGroupController=</varname></term>
|
||||
<term><varname>AssertMemory=</varname></term>
|
||||
<term><varname>AssertCPUs=</varname></term>
|
||||
<term><varname>AssertOSRelease=</varname></term>
|
||||
|
||||
<listitem><para>Similar to the <varname>ConditionArchitecture=</varname>,
|
||||
<varname>ConditionVirtualization=</varname>, …, condition settings described above, these settings
|
||||
|
@ -330,6 +330,7 @@ Unit.ConditionEnvironment, config_parse_unit_condition_string,
|
||||
Unit.ConditionUser, config_parse_unit_condition_string, CONDITION_USER, offsetof(Unit, conditions)
|
||||
Unit.ConditionGroup, config_parse_unit_condition_string, CONDITION_GROUP, offsetof(Unit, conditions)
|
||||
Unit.ConditionControlGroupController, config_parse_unit_condition_string, CONDITION_CONTROL_GROUP_CONTROLLER, offsetof(Unit, conditions)
|
||||
Unit.ConditionOSRelease, config_parse_unit_condition_string, CONDITION_OS_RELEASE, 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)
|
||||
@ -356,6 +357,7 @@ Unit.AssertEnvironment, config_parse_unit_condition_string,
|
||||
Unit.AssertUser, config_parse_unit_condition_string, CONDITION_USER, offsetof(Unit, asserts)
|
||||
Unit.AssertGroup, config_parse_unit_condition_string, CONDITION_GROUP, offsetof(Unit, asserts)
|
||||
Unit.AssertControlGroupController, config_parse_unit_condition_string, CONDITION_CONTROL_GROUP_CONTROLLER, offsetof(Unit, asserts)
|
||||
Unit.AssertOSRelease, config_parse_unit_condition_string, CONDITION_OS_RELEASE, offsetof(Unit, asserts)
|
||||
Unit.CollectMode, config_parse_collect_mode, 0, offsetof(Unit, collect_mode)
|
||||
Service.PIDFile, config_parse_pid_file, 0, offsetof(Service, pid_file)
|
||||
Service.ExecCondition, config_parse_exec, SERVICE_EXEC_CONDITION, offsetof(Service, exec_command)
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "cpu-set-util.h"
|
||||
#include "efi-loader.h"
|
||||
#include "env-file.h"
|
||||
#include "env-util.h"
|
||||
#include "extract-word.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
@ -34,6 +35,7 @@
|
||||
#include "list.h"
|
||||
#include "macro.h"
|
||||
#include "mountpoint-util.h"
|
||||
#include "os-util.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "proc-cmdline.h"
|
||||
@ -262,6 +264,61 @@ static int condition_test_kernel_version(Condition *c, char **env) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static int condition_test_osrelease(Condition *c, char **env) {
|
||||
const char *parameter = c->parameter;
|
||||
int r;
|
||||
|
||||
assert(c);
|
||||
assert(c->parameter);
|
||||
assert(c->type == CONDITION_OS_RELEASE);
|
||||
|
||||
for (;;) {
|
||||
_cleanup_free_ char *key = NULL, *condition = NULL, *actual_value = NULL;
|
||||
OrderOperator order;
|
||||
const char *word;
|
||||
bool matches;
|
||||
|
||||
r = extract_first_word(¶meter, &condition, NULL, EXTRACT_UNQUOTE);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to parse parameter: %m");
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
/* parse_order() needs the string to start with the comparators */
|
||||
word = condition;
|
||||
r = extract_first_word(&word, &key, "!<=>", EXTRACT_RETAIN_SEPARATORS);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to parse parameter: %m");
|
||||
/* The os-release spec mandates env-var-like key names */
|
||||
if (r == 0 || isempty(word) || !env_name_is_valid(key))
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Failed to parse parameter, key/value format expected: %m");
|
||||
|
||||
/* Do not allow whitespace after the separator, as that's not a valid os-release format */
|
||||
order = parse_order(&word);
|
||||
if (order < 0 || isempty(word) || strchr(WHITESPACE, *word) != NULL)
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Failed to parse parameter, key/value format expected: %m");
|
||||
|
||||
r = parse_os_release(NULL, key, &actual_value);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to parse os-release: %m");
|
||||
|
||||
/* Might not be comparing versions, so do exact string matching */
|
||||
if (order == ORDER_EQUAL)
|
||||
matches = streq_ptr(actual_value, word);
|
||||
else if (order == ORDER_UNEQUAL)
|
||||
matches = !streq_ptr(actual_value, word);
|
||||
else
|
||||
matches = test_order(strverscmp_improved(actual_value, word), order);
|
||||
|
||||
if (!matches)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int condition_test_memory(Condition *c, char **env) {
|
||||
OrderOperator order;
|
||||
uint64_t m, k;
|
||||
@ -934,6 +991,7 @@ int condition_test(Condition *c, char **env) {
|
||||
[CONDITION_MEMORY] = condition_test_memory,
|
||||
[CONDITION_ENVIRONMENT] = condition_test_environment,
|
||||
[CONDITION_CPU_FEATURE] = condition_test_cpufeature,
|
||||
[CONDITION_OS_RELEASE] = condition_test_osrelease,
|
||||
};
|
||||
|
||||
int r, b;
|
||||
@ -1058,6 +1116,7 @@ static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
|
||||
[CONDITION_MEMORY] = "ConditionMemory",
|
||||
[CONDITION_ENVIRONMENT] = "ConditionEnvironment",
|
||||
[CONDITION_CPU_FEATURE] = "ConditionCPUFeature",
|
||||
[CONDITION_OS_RELEASE] = "ConditionOSRelease",
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType);
|
||||
@ -1091,6 +1150,7 @@ static const char* const assert_type_table[_CONDITION_TYPE_MAX] = {
|
||||
[CONDITION_MEMORY] = "AssertMemory",
|
||||
[CONDITION_ENVIRONMENT] = "AssertEnvironment",
|
||||
[CONDITION_CPU_FEATURE] = "AssertCPUFeature",
|
||||
[CONDITION_OS_RELEASE] = "AssertOSRelease",
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(assert_type, ConditionType);
|
||||
|
@ -21,6 +21,7 @@ typedef enum ConditionType {
|
||||
CONDITION_CPUS,
|
||||
CONDITION_ENVIRONMENT,
|
||||
CONDITION_CPU_FEATURE,
|
||||
CONDITION_OS_RELEASE,
|
||||
|
||||
CONDITION_NEEDS_UPDATE,
|
||||
CONDITION_FIRST_BOOT,
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "log.h"
|
||||
#include "macro.h"
|
||||
#include "nulstr-util.h"
|
||||
#include "os-util.h"
|
||||
#include "process-util.h"
|
||||
#include "selinux-util.h"
|
||||
#include "set.h"
|
||||
@ -890,6 +891,150 @@ static void test_condition_test_environment(void) {
|
||||
test_condition_test_environment_one("EXISTINGENVVAR=", false);
|
||||
}
|
||||
|
||||
static void test_condition_test_os_release(void) {
|
||||
_cleanup_strv_free_ char **os_release_pairs = NULL;
|
||||
_cleanup_free_ char *version_id = NULL;
|
||||
const char *key_value_pair;
|
||||
Condition *condition;
|
||||
|
||||
/* Should not happen, but it's a test so we don't know the environment. */
|
||||
if (load_os_release_pairs(NULL, &os_release_pairs) < 0)
|
||||
return;
|
||||
if (strv_length(os_release_pairs) < 2)
|
||||
return;
|
||||
|
||||
condition = condition_new(CONDITION_OS_RELEASE, "_THISHOPEFULLYWONTEXIST=01234 56789", false, false);
|
||||
assert_se(condition);
|
||||
assert_se(condition_test(condition, environ) == 0);
|
||||
condition_free(condition);
|
||||
|
||||
condition = condition_new(CONDITION_OS_RELEASE, "WRONG FORMAT", false, false);
|
||||
assert_se(condition);
|
||||
assert_se(condition_test(condition, environ) == -EINVAL);
|
||||
condition_free(condition);
|
||||
|
||||
condition = condition_new(CONDITION_OS_RELEASE, "WRONG!<>=FORMAT", false, false);
|
||||
assert_se(condition);
|
||||
assert_se(condition_test(condition, environ) == -EINVAL);
|
||||
condition_free(condition);
|
||||
|
||||
condition = condition_new(CONDITION_OS_RELEASE, "WRONG FORMAT=", false, false);
|
||||
assert_se(condition);
|
||||
assert_se(condition_test(condition, environ) == -EINVAL);
|
||||
condition_free(condition);
|
||||
|
||||
condition = condition_new(CONDITION_OS_RELEASE, "WRONG =FORMAT", false, false);
|
||||
assert_se(condition);
|
||||
assert_se(condition_test(condition, environ) == -EINVAL);
|
||||
condition_free(condition);
|
||||
|
||||
condition = condition_new(CONDITION_OS_RELEASE, "WRONG = FORMAT", false, false);
|
||||
assert_se(condition);
|
||||
assert_se(condition_test(condition, environ) == -EINVAL);
|
||||
condition_free(condition);
|
||||
|
||||
condition = condition_new(CONDITION_OS_RELEASE, "WRONGFORMAT= ", false, false);
|
||||
assert_se(condition);
|
||||
assert_se(condition_test(condition, environ) == -EINVAL);
|
||||
condition_free(condition);
|
||||
|
||||
condition = condition_new(CONDITION_OS_RELEASE, "WRO NG=FORMAT", false, false);
|
||||
assert_se(condition);
|
||||
assert_se(condition_test(condition, environ) == -EINVAL);
|
||||
condition_free(condition);
|
||||
|
||||
condition = condition_new(CONDITION_OS_RELEASE, "", false, false);
|
||||
assert_se(condition);
|
||||
assert_se(condition_test(condition, environ));
|
||||
condition_free(condition);
|
||||
|
||||
/* load_os_release_pairs() removes quotes, we have to add them back,
|
||||
* otherwise we get a string: "PRETTY_NAME=Debian GNU/Linux 10 (buster)"
|
||||
* which is wrong, as the value is not quoted anymore. */
|
||||
const char *quote = strchr(os_release_pairs[1], ' ') ? "\"" : "";
|
||||
key_value_pair = strjoina(os_release_pairs[0], "=", quote, os_release_pairs[1], quote);
|
||||
condition = condition_new(CONDITION_OS_RELEASE, key_value_pair, false, false);
|
||||
assert_se(condition);
|
||||
assert_se(condition_test(condition, environ));
|
||||
condition_free(condition);
|
||||
|
||||
key_value_pair = strjoina(os_release_pairs[0], "!=", quote, os_release_pairs[1], quote);
|
||||
condition = condition_new(CONDITION_OS_RELEASE, key_value_pair, false, false);
|
||||
assert_se(condition);
|
||||
assert_se(condition_test(condition, environ) == 0);
|
||||
condition_free(condition);
|
||||
|
||||
/* Some distros (eg: Arch) do not set VERSION_ID */
|
||||
if (parse_os_release(NULL, "VERSION_ID", &version_id) <= 0)
|
||||
return;
|
||||
|
||||
key_value_pair = strjoina("VERSION_ID", "=", version_id);
|
||||
condition = condition_new(CONDITION_OS_RELEASE, key_value_pair, false, false);
|
||||
assert_se(condition);
|
||||
assert_se(condition_test(condition, environ));
|
||||
condition_free(condition);
|
||||
|
||||
key_value_pair = strjoina("VERSION_ID", "!=", version_id);
|
||||
condition = condition_new(CONDITION_OS_RELEASE, key_value_pair, false, false);
|
||||
assert_se(condition);
|
||||
assert_se(condition_test(condition, environ) == 0);
|
||||
condition_free(condition);
|
||||
|
||||
key_value_pair = strjoina("VERSION_ID", "<=", version_id);
|
||||
condition = condition_new(CONDITION_OS_RELEASE, key_value_pair, false, false);
|
||||
assert_se(condition);
|
||||
assert_se(condition_test(condition, environ));
|
||||
condition_free(condition);
|
||||
|
||||
key_value_pair = strjoina("VERSION_ID", ">=", version_id);
|
||||
condition = condition_new(CONDITION_OS_RELEASE, key_value_pair, false, false);
|
||||
assert_se(condition);
|
||||
assert_se(condition_test(condition, environ));
|
||||
condition_free(condition);
|
||||
|
||||
key_value_pair = strjoina("VERSION_ID", "<", version_id, ".1");
|
||||
condition = condition_new(CONDITION_OS_RELEASE, key_value_pair, false, false);
|
||||
assert_se(condition);
|
||||
assert_se(condition_test(condition, environ));
|
||||
condition_free(condition);
|
||||
|
||||
key_value_pair = strjoina("VERSION_ID", ">", version_id, ".1");
|
||||
condition = condition_new(CONDITION_OS_RELEASE, key_value_pair, false, false);
|
||||
assert_se(condition);
|
||||
assert_se(condition_test(condition, environ) == 0);
|
||||
condition_free(condition);
|
||||
|
||||
key_value_pair = strjoina("VERSION_ID", "=", version_id, " ", os_release_pairs[0], "=", quote, os_release_pairs[1], quote);
|
||||
condition = condition_new(CONDITION_OS_RELEASE, key_value_pair, false, false);
|
||||
assert_se(condition);
|
||||
assert_se(condition_test(condition, environ));
|
||||
condition_free(condition);
|
||||
|
||||
key_value_pair = strjoina("VERSION_ID", "!=", version_id, " ", os_release_pairs[0], "=", quote, os_release_pairs[1], quote);
|
||||
condition = condition_new(CONDITION_OS_RELEASE, key_value_pair, false, false);
|
||||
assert_se(condition);
|
||||
assert_se(condition_test(condition, environ) == 0);
|
||||
condition_free(condition);
|
||||
|
||||
key_value_pair = strjoina("VERSION_ID", "=", version_id, " ", os_release_pairs[0], "!=", quote, os_release_pairs[1], quote);
|
||||
condition = condition_new(CONDITION_OS_RELEASE, key_value_pair, false, false);
|
||||
assert_se(condition);
|
||||
assert_se(condition_test(condition, environ) == 0);
|
||||
condition_free(condition);
|
||||
|
||||
key_value_pair = strjoina("VERSION_ID", "!=", version_id, " ", os_release_pairs[0], "!=", quote, os_release_pairs[1], quote);
|
||||
condition = condition_new(CONDITION_OS_RELEASE, key_value_pair, false, false);
|
||||
assert_se(condition);
|
||||
assert_se(condition_test(condition, environ) == 0);
|
||||
condition_free(condition);
|
||||
|
||||
key_value_pair = strjoina("VERSION_ID", "<", version_id, ".1", " ", os_release_pairs[0], "=", quote, os_release_pairs[1], quote);
|
||||
condition = condition_new(CONDITION_OS_RELEASE, key_value_pair, false, false);
|
||||
assert_se(condition);
|
||||
assert_se(condition_test(condition, environ));
|
||||
condition_free(condition);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
test_setup_logging(LOG_DEBUG);
|
||||
|
||||
@ -912,6 +1057,7 @@ int main(int argc, char *argv[]) {
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
test_condition_test_cpufeature();
|
||||
#endif
|
||||
test_condition_test_os_release();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ AssertHost=
|
||||
AssertKernelCommandLine=
|
||||
AssertKernelVersion=
|
||||
AssertNeedsUpdate=
|
||||
AssertOSRelease=
|
||||
AssertPathExists=
|
||||
AssertPathExistsGlob=
|
||||
AssertPathIsDirectory=
|
||||
@ -64,6 +65,7 @@ ConditionHost=
|
||||
ConditionKernelCommandLine=
|
||||
ConditionKernelVersion=
|
||||
ConditionNeedsUpdate=
|
||||
ConditionOSRelease=
|
||||
ConditionPathExists=
|
||||
ConditionPathExistsGlob=
|
||||
ConditionPathIsDirectory=
|
||||
|
@ -18,6 +18,7 @@ AssertKernelCommandLine=
|
||||
AssertKernelVersion=
|
||||
AssertMemory=
|
||||
AssertNeedsUpdate=
|
||||
AssertOSRelease=
|
||||
AssertPathExists=
|
||||
AssertPathExistsGlob=
|
||||
AssertPathIsDirectory=
|
||||
@ -50,6 +51,7 @@ ConditionKernelCommandLine=
|
||||
ConditionKernelVersion=
|
||||
ConditionMemory=
|
||||
ConditionNeedsUpdate=
|
||||
ConditionOSRelease=
|
||||
ConditionPathExists=
|
||||
ConditionPathExistsGlob=
|
||||
ConditionPathIsDirectory=
|
||||
|
Loading…
Reference in New Issue
Block a user