X Tutup
Skip to content

Commit a2e2917

Browse files
authored
Merge pull request systemd#19941 from bluca/condition_os_release
core: add ConditionOSRelease= directive
2 parents de61a04 + 1e26f8a commit a2e2917

File tree

10 files changed

+292
-9
lines changed

10 files changed

+292
-9
lines changed

man/systemd.unit.xml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1646,6 +1646,19 @@
16461646
</listitem>
16471647
</varlistentry>
16481648

1649+
<varlistentry>
1650+
<term><varname>ConditionOSRelease=</varname></term>
1651+
1652+
<listitem><para>Verify that a specific <literal>key=value</literal> pair is set in the host's
1653+
<citerefentry><refentrytitle>os-release</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
1654+
1655+
<para>Other than exact matching with <literal>=</literal>, and <literal>!=</literal>, relative
1656+
comparisons are supported for versioned parameters (e.g. <literal>VERSION_ID</literal>). The
1657+
comparator can be one of <literal>&lt;</literal>, <literal>&lt;=</literal>, <literal>=</literal>,
1658+
<literal>!=</literal>, <literal>&gt;=</literal> and <literal>&gt;</literal>.</para>
1659+
</listitem>
1660+
</varlistentry>
1661+
16491662
<varlistentry>
16501663
<term><varname>AssertArchitecture=</varname></term>
16511664
<term><varname>AssertVirtualization=</varname></term>
@@ -1673,6 +1686,7 @@
16731686
<term><varname>AssertControlGroupController=</varname></term>
16741687
<term><varname>AssertMemory=</varname></term>
16751688
<term><varname>AssertCPUs=</varname></term>
1689+
<term><varname>AssertOSRelease=</varname></term>
16761690

16771691
<listitem><para>Similar to the <varname>ConditionArchitecture=</varname>,
16781692
<varname>ConditionVirtualization=</varname>, …, condition settings described above, these settings

src/basic/extract-word.c

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra
5151
goto finish_force_terminate;
5252
else if (strchr(separators, c)) {
5353
if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) {
54-
(*p)++;
54+
if (!(flags & EXTRACT_RETAIN_SEPARATORS))
55+
(*p)++;
5556
goto finish_force_next;
5657
}
5758
} else {
@@ -153,16 +154,18 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra
153154
break;
154155
} else if (strchr(separators, c)) {
155156
if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) {
156-
(*p)++;
157+
if (!(flags & EXTRACT_RETAIN_SEPARATORS))
158+
(*p)++;
157159
goto finish_force_next;
158160
}
159-
/* Skip additional coalesced separators. */
160-
for (;; (*p)++, c = **p) {
161-
if (c == 0)
162-
goto finish_force_terminate;
163-
if (!strchr(separators, c))
164-
break;
165-
}
161+
if (!(flags & EXTRACT_RETAIN_SEPARATORS))
162+
/* Skip additional coalesced separators. */
163+
for (;; (*p)++, c = **p) {
164+
if (c == 0)
165+
goto finish_force_terminate;
166+
if (!strchr(separators, c))
167+
break;
168+
}
166169
goto finish;
167170

168171
}

src/basic/extract-word.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ typedef enum ExtractFlags {
1212
EXTRACT_UNQUOTE = 1 << 5, /* Ignore separators in quoting with "" and '', and remove the quotes. */
1313
EXTRACT_DONT_COALESCE_SEPARATORS = 1 << 6, /* Don't treat multiple adjacent separators as one */
1414
EXTRACT_RETAIN_ESCAPE = 1 << 7, /* Treat escape character '\' as any other character without special meaning */
15+
EXTRACT_RETAIN_SEPARATORS = 1 << 8, /* Do not advance the original string pointer past the separator(s) */
1516

1617
/* Note that if no flags are specified, escaped escape characters will be silently stripped. */
1718
} ExtractFlags;

src/core/load-fragment-gperf.gperf.in

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,7 @@ Unit.ConditionEnvironment, config_parse_unit_condition_string,
330330
Unit.ConditionUser, config_parse_unit_condition_string, CONDITION_USER, offsetof(Unit, conditions)
331331
Unit.ConditionGroup, config_parse_unit_condition_string, CONDITION_GROUP, offsetof(Unit, conditions)
332332
Unit.ConditionControlGroupController, config_parse_unit_condition_string, CONDITION_CONTROL_GROUP_CONTROLLER, offsetof(Unit, conditions)
333+
Unit.ConditionOSRelease, config_parse_unit_condition_string, CONDITION_OS_RELEASE, offsetof(Unit, conditions)
333334
Unit.AssertPathExists, config_parse_unit_condition_path, CONDITION_PATH_EXISTS, offsetof(Unit, asserts)
334335
Unit.AssertPathExistsGlob, config_parse_unit_condition_path, CONDITION_PATH_EXISTS_GLOB, offsetof(Unit, asserts)
335336
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,
356357
Unit.AssertUser, config_parse_unit_condition_string, CONDITION_USER, offsetof(Unit, asserts)
357358
Unit.AssertGroup, config_parse_unit_condition_string, CONDITION_GROUP, offsetof(Unit, asserts)
358359
Unit.AssertControlGroupController, config_parse_unit_condition_string, CONDITION_CONTROL_GROUP_CONTROLLER, offsetof(Unit, asserts)
360+
Unit.AssertOSRelease, config_parse_unit_condition_string, CONDITION_OS_RELEASE, offsetof(Unit, asserts)
359361
Unit.CollectMode, config_parse_collect_mode, 0, offsetof(Unit, collect_mode)
360362
Service.PIDFile, config_parse_pid_file, 0, offsetof(Service, pid_file)
361363
Service.ExecCondition, config_parse_exec, SERVICE_EXEC_CONDITION, offsetof(Service, exec_command)

src/shared/condition.c

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "cpu-set-util.h"
2525
#include "efi-loader.h"
2626
#include "env-file.h"
27+
#include "env-util.h"
2728
#include "extract-word.h"
2829
#include "fd-util.h"
2930
#include "fileio.h"
@@ -35,6 +36,7 @@
3536
#include "list.h"
3637
#include "macro.h"
3738
#include "mountpoint-util.h"
39+
#include "os-util.h"
3840
#include "parse-util.h"
3941
#include "path-util.h"
4042
#include "proc-cmdline.h"
@@ -263,6 +265,61 @@ static int condition_test_kernel_version(Condition *c, char **env) {
263265
return true;
264266
}
265267

268+
static int condition_test_osrelease(Condition *c, char **env) {
269+
const char *parameter = c->parameter;
270+
int r;
271+
272+
assert(c);
273+
assert(c->parameter);
274+
assert(c->type == CONDITION_OS_RELEASE);
275+
276+
for (;;) {
277+
_cleanup_free_ char *key = NULL, *condition = NULL, *actual_value = NULL;
278+
OrderOperator order;
279+
const char *word;
280+
bool matches;
281+
282+
r = extract_first_word(&parameter, &condition, NULL, EXTRACT_UNQUOTE);
283+
if (r < 0)
284+
return log_debug_errno(r, "Failed to parse parameter: %m");
285+
if (r == 0)
286+
break;
287+
288+
/* parse_order() needs the string to start with the comparators */
289+
word = condition;
290+
r = extract_first_word(&word, &key, "!<=>", EXTRACT_RETAIN_SEPARATORS);
291+
if (r < 0)
292+
return log_debug_errno(r, "Failed to parse parameter: %m");
293+
/* The os-release spec mandates env-var-like key names */
294+
if (r == 0 || isempty(word) || !env_name_is_valid(key))
295+
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
296+
"Failed to parse parameter, key/value format expected: %m");
297+
298+
/* Do not allow whitespace after the separator, as that's not a valid os-release format */
299+
order = parse_order(&word);
300+
if (order < 0 || isempty(word) || strchr(WHITESPACE, *word) != NULL)
301+
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
302+
"Failed to parse parameter, key/value format expected: %m");
303+
304+
r = parse_os_release(NULL, key, &actual_value);
305+
if (r < 0)
306+
return log_debug_errno(r, "Failed to parse os-release: %m");
307+
308+
/* Might not be comparing versions, so do exact string matching */
309+
if (order == ORDER_EQUAL)
310+
matches = streq_ptr(actual_value, word);
311+
else if (order == ORDER_UNEQUAL)
312+
matches = !streq_ptr(actual_value, word);
313+
else
314+
matches = test_order(strverscmp_improved(actual_value, word), order);
315+
316+
if (!matches)
317+
return false;
318+
}
319+
320+
return true;
321+
}
322+
266323
static int condition_test_memory(Condition *c, char **env) {
267324
OrderOperator order;
268325
uint64_t m, k;
@@ -935,6 +992,7 @@ int condition_test(Condition *c, char **env) {
935992
[CONDITION_MEMORY] = condition_test_memory,
936993
[CONDITION_ENVIRONMENT] = condition_test_environment,
937994
[CONDITION_CPU_FEATURE] = condition_test_cpufeature,
995+
[CONDITION_OS_RELEASE] = condition_test_osrelease,
938996
};
939997

940998
int r, b;
@@ -1059,6 +1117,7 @@ static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
10591117
[CONDITION_MEMORY] = "ConditionMemory",
10601118
[CONDITION_ENVIRONMENT] = "ConditionEnvironment",
10611119
[CONDITION_CPU_FEATURE] = "ConditionCPUFeature",
1120+
[CONDITION_OS_RELEASE] = "ConditionOSRelease",
10621121
};
10631122

10641123
DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType);
@@ -1092,6 +1151,7 @@ static const char* const assert_type_table[_CONDITION_TYPE_MAX] = {
10921151
[CONDITION_MEMORY] = "AssertMemory",
10931152
[CONDITION_ENVIRONMENT] = "AssertEnvironment",
10941153
[CONDITION_CPU_FEATURE] = "AssertCPUFeature",
1154+
[CONDITION_OS_RELEASE] = "AssertOSRelease",
10951155
};
10961156

10971157
DEFINE_STRING_TABLE_LOOKUP(assert_type, ConditionType);

src/shared/condition.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ typedef enum ConditionType {
2121
CONDITION_CPUS,
2222
CONDITION_ENVIRONMENT,
2323
CONDITION_CPU_FEATURE,
24+
CONDITION_OS_RELEASE,
2425

2526
CONDITION_NEEDS_UPDATE,
2627
CONDITION_FIRST_BOOT,

src/test/test-condition.c

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "log.h"
2424
#include "macro.h"
2525
#include "nulstr-util.h"
26+
#include "os-util.h"
2627
#include "process-util.h"
2728
#include "selinux-util.h"
2829
#include "set.h"
@@ -890,6 +891,150 @@ static void test_condition_test_environment(void) {
890891
test_condition_test_environment_one("EXISTINGENVVAR=", false);
891892
}
892893

894+
static void test_condition_test_os_release(void) {
895+
_cleanup_strv_free_ char **os_release_pairs = NULL;
896+
_cleanup_free_ char *version_id = NULL;
897+
const char *key_value_pair;
898+
Condition *condition;
899+
900+
/* Should not happen, but it's a test so we don't know the environment. */
901+
if (load_os_release_pairs(NULL, &os_release_pairs) < 0)
902+
return;
903+
if (strv_length(os_release_pairs) < 2)
904+
return;
905+
906+
condition = condition_new(CONDITION_OS_RELEASE, "_THISHOPEFULLYWONTEXIST=01234 56789", false, false);
907+
assert_se(condition);
908+
assert_se(condition_test(condition, environ) == 0);
909+
condition_free(condition);
910+
911+
condition = condition_new(CONDITION_OS_RELEASE, "WRONG FORMAT", false, false);
912+
assert_se(condition);
913+
assert_se(condition_test(condition, environ) == -EINVAL);
914+
condition_free(condition);
915+
916+
condition = condition_new(CONDITION_OS_RELEASE, "WRONG!<>=FORMAT", false, false);
917+
assert_se(condition);
918+
assert_se(condition_test(condition, environ) == -EINVAL);
919+
condition_free(condition);
920+
921+
condition = condition_new(CONDITION_OS_RELEASE, "WRONG FORMAT=", false, false);
922+
assert_se(condition);
923+
assert_se(condition_test(condition, environ) == -EINVAL);
924+
condition_free(condition);
925+
926+
condition = condition_new(CONDITION_OS_RELEASE, "WRONG =FORMAT", false, false);
927+
assert_se(condition);
928+
assert_se(condition_test(condition, environ) == -EINVAL);
929+
condition_free(condition);
930+
931+
condition = condition_new(CONDITION_OS_RELEASE, "WRONG = FORMAT", false, false);
932+
assert_se(condition);
933+
assert_se(condition_test(condition, environ) == -EINVAL);
934+
condition_free(condition);
935+
936+
condition = condition_new(CONDITION_OS_RELEASE, "WRONGFORMAT= ", false, false);
937+
assert_se(condition);
938+
assert_se(condition_test(condition, environ) == -EINVAL);
939+
condition_free(condition);
940+
941+
condition = condition_new(CONDITION_OS_RELEASE, "WRO NG=FORMAT", false, false);
942+
assert_se(condition);
943+
assert_se(condition_test(condition, environ) == -EINVAL);
944+
condition_free(condition);
945+
946+
condition = condition_new(CONDITION_OS_RELEASE, "", false, false);
947+
assert_se(condition);
948+
assert_se(condition_test(condition, environ));
949+
condition_free(condition);
950+
951+
/* load_os_release_pairs() removes quotes, we have to add them back,
952+
* otherwise we get a string: "PRETTY_NAME=Debian GNU/Linux 10 (buster)"
953+
* which is wrong, as the value is not quoted anymore. */
954+
const char *quote = strchr(os_release_pairs[1], ' ') ? "\"" : "";
955+
key_value_pair = strjoina(os_release_pairs[0], "=", quote, os_release_pairs[1], quote);
956+
condition = condition_new(CONDITION_OS_RELEASE, key_value_pair, false, false);
957+
assert_se(condition);
958+
assert_se(condition_test(condition, environ));
959+
condition_free(condition);
960+
961+
key_value_pair = strjoina(os_release_pairs[0], "!=", quote, os_release_pairs[1], quote);
962+
condition = condition_new(CONDITION_OS_RELEASE, key_value_pair, false, false);
963+
assert_se(condition);
964+
assert_se(condition_test(condition, environ) == 0);
965+
condition_free(condition);
966+
967+
/* Some distros (eg: Arch) do not set VERSION_ID */
968+
if (parse_os_release(NULL, "VERSION_ID", &version_id) <= 0)
969+
return;
970+
971+
key_value_pair = strjoina("VERSION_ID", "=", version_id);
972+
condition = condition_new(CONDITION_OS_RELEASE, key_value_pair, false, false);
973+
assert_se(condition);
974+
assert_se(condition_test(condition, environ));
975+
condition_free(condition);
976+
977+
key_value_pair = strjoina("VERSION_ID", "!=", version_id);
978+
condition = condition_new(CONDITION_OS_RELEASE, key_value_pair, false, false);
979+
assert_se(condition);
980+
assert_se(condition_test(condition, environ) == 0);
981+
condition_free(condition);
982+
983+
key_value_pair = strjoina("VERSION_ID", "<=", version_id);
984+
condition = condition_new(CONDITION_OS_RELEASE, key_value_pair, false, false);
985+
assert_se(condition);
986+
assert_se(condition_test(condition, environ));
987+
condition_free(condition);
988+
989+
key_value_pair = strjoina("VERSION_ID", ">=", version_id);
990+
condition = condition_new(CONDITION_OS_RELEASE, key_value_pair, false, false);
991+
assert_se(condition);
992+
assert_se(condition_test(condition, environ));
993+
condition_free(condition);
994+
995+
key_value_pair = strjoina("VERSION_ID", "<", version_id, ".1");
996+
condition = condition_new(CONDITION_OS_RELEASE, key_value_pair, false, false);
997+
assert_se(condition);
998+
assert_se(condition_test(condition, environ));
999+
condition_free(condition);
1000+
1001+
key_value_pair = strjoina("VERSION_ID", ">", version_id, ".1");
1002+
condition = condition_new(CONDITION_OS_RELEASE, key_value_pair, false, false);
1003+
assert_se(condition);
1004+
assert_se(condition_test(condition, environ) == 0);
1005+
condition_free(condition);
1006+
1007+
key_value_pair = strjoina("VERSION_ID", "=", version_id, " ", os_release_pairs[0], "=", quote, os_release_pairs[1], quote);
1008+
condition = condition_new(CONDITION_OS_RELEASE, key_value_pair, false, false);
1009+
assert_se(condition);
1010+
assert_se(condition_test(condition, environ));
1011+
condition_free(condition);
1012+
1013+
key_value_pair = strjoina("VERSION_ID", "!=", version_id, " ", os_release_pairs[0], "=", quote, os_release_pairs[1], quote);
1014+
condition = condition_new(CONDITION_OS_RELEASE, key_value_pair, false, false);
1015+
assert_se(condition);
1016+
assert_se(condition_test(condition, environ) == 0);
1017+
condition_free(condition);
1018+
1019+
key_value_pair = strjoina("VERSION_ID", "=", version_id, " ", os_release_pairs[0], "!=", quote, os_release_pairs[1], quote);
1020+
condition = condition_new(CONDITION_OS_RELEASE, key_value_pair, false, false);
1021+
assert_se(condition);
1022+
assert_se(condition_test(condition, environ) == 0);
1023+
condition_free(condition);
1024+
1025+
key_value_pair = strjoina("VERSION_ID", "!=", version_id, " ", os_release_pairs[0], "!=", quote, os_release_pairs[1], quote);
1026+
condition = condition_new(CONDITION_OS_RELEASE, key_value_pair, false, false);
1027+
assert_se(condition);
1028+
assert_se(condition_test(condition, environ) == 0);
1029+
condition_free(condition);
1030+
1031+
key_value_pair = strjoina("VERSION_ID", "<", version_id, ".1", " ", os_release_pairs[0], "=", quote, os_release_pairs[1], quote);
1032+
condition = condition_new(CONDITION_OS_RELEASE, key_value_pair, false, false);
1033+
assert_se(condition);
1034+
assert_se(condition_test(condition, environ));
1035+
condition_free(condition);
1036+
}
1037+
8931038
int main(int argc, char *argv[]) {
8941039
test_setup_logging(LOG_DEBUG);
8951040

@@ -912,6 +1057,7 @@ int main(int argc, char *argv[]) {
9121057
#if defined(__i386__) || defined(__x86_64__)
9131058
test_condition_test_cpufeature();
9141059
#endif
1060+
test_condition_test_os_release();
9151061

9161062
return 0;
9171063
}

0 commit comments

Comments
 (0)
X Tutup