]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/condition.c
man/run0: Describe environment variables set (#32622)
[thirdparty/systemd.git] / src / shared / condition.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
b77c08e0 2
bf07a125 3#include <ctype.h>
b77c08e0 4#include <errno.h>
a8fbdf54 5#include <fcntl.h>
84ac7bea 6#include <fnmatch.h>
a8fbdf54 7#include <limits.h>
84ac7bea 8#include <stdlib.h>
cbcdcaaa 9#include <sys/stat.h>
c465a29f 10#include <sys/types.h>
5022f08a 11#include <sys/utsname.h>
a8fbdf54 12#include <time.h>
b77c08e0 13#include <unistd.h>
b77c08e0 14
d1bddcec 15#include "sd-id128.h"
84ac7bea 16
b5efdb8a 17#include "alloc-util.h"
d1bddcec 18#include "apparmor-util.h"
84ac7bea 19#include "architecture.h"
430f0182 20#include "audit-util.h"
af4e8e86 21#include "battery-util.h"
b25a930f 22#include "blockdev-util.h"
2822da4f 23#include "cap-list.h"
e16647c3 24#include "cgroup-util.h"
a8835c11 25#include "compare-operator.h"
3ffd4af2 26#include "condition.h"
95d043b1 27#include "confidential-virt.h"
f44b3035 28#include "cpu-set-util.h"
4f80cfca 29#include "creds-util.h"
7be4b236 30#include "efi-api.h"
340d979a 31#include "efi-loader.h"
78d76525 32#include "env-file.h"
1e26f8a6 33#include "env-util.h"
84ac7bea 34#include "extract-word.h"
3ffd4af2 35#include "fd-util.h"
fb8b0869 36#include "fileio.h"
7f19247b 37#include "fs-util.h"
7d50b32a 38#include "glob-util.h"
958b66ea 39#include "hostname-util.h"
84ac7bea 40#include "ima-util.h"
08af3cc5 41#include "initrd-util.h"
754f719a 42#include "limits-util.h"
a8fbdf54
TA
43#include "list.h"
44#include "macro.h"
049af8ad 45#include "mountpoint-util.h"
08af3cc5 46#include "nulstr-util.h"
1e26f8a6 47#include "os-util.h"
6bedfcbb 48#include "parse-util.h"
84ac7bea 49#include "path-util.h"
81513b38 50#include "percent-util.h"
4e731273 51#include "proc-cmdline.h"
df0ff127 52#include "process-util.h"
81513b38 53#include "psi-util.h"
84ac7bea
LP
54#include "selinux-util.h"
55#include "smack-util.h"
68ee5d77 56#include "special.h"
8fcde012 57#include "stat-util.h"
8b43440b 58#include "string-table.h"
07630cea 59#include "string-util.h"
ed440f6b 60#include "tomoyo-util.h"
e1be2c77 61#include "tpm2-util.h"
8e1ac16b 62#include "uid-classification.h"
c465a29f 63#include "user-util.h"
84ac7bea 64#include "virt.h"
b77c08e0
TG
65
66Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate) {
67 Condition *c;
68
b80ba1da 69 assert(type >= 0);
b77c08e0 70 assert(type < _CONDITION_TYPE_MAX);
476cfe62 71 assert(parameter);
b77c08e0 72
78d76525 73 c = new(Condition, 1);
b77c08e0
TG
74 if (!c)
75 return NULL;
76
78d76525
LP
77 *c = (Condition) {
78 .type = type,
79 .trigger = trigger,
80 .negate = negate,
81 };
b77c08e0 82
78d76525
LP
83 if (parameter) {
84 c->parameter = strdup(parameter);
85 if (!c->parameter)
86 return mfree(c);
b77c08e0
TG
87 }
88
89 return c;
90}
91
411e835c 92Condition* condition_free(Condition *c) {
b77c08e0
TG
93 assert(c);
94
95 free(c->parameter);
411e835c 96 return mfree(c);
b77c08e0
TG
97}
98
7bb55ed0 99Condition* condition_free_list_type(Condition *head, ConditionType type) {
80a226b2 100 LIST_FOREACH(conditions, c, head)
7bb55ed0
YW
101 if (type < 0 || c->type == type) {
102 LIST_REMOVE(conditions, head, c);
c4f58dea 103 condition_free(c);
7bb55ed0 104 }
447021aa 105
7bb55ed0
YW
106 assert(type >= 0 || !head);
107 return head;
b77c08e0
TG
108}
109
a0b191b7 110static int condition_test_kernel_command_line(Condition *c, char **env) {
dd2d3e97 111 _cleanup_strv_free_ char **args = NULL;
b77c08e0 112 int r;
b77c08e0
TG
113
114 assert(c);
115 assert(c->parameter);
116 assert(c->type == CONDITION_KERNEL_COMMAND_LINE);
117
dd2d3e97 118 r = proc_cmdline_strv(&args);
b77c08e0 119 if (r < 0)
592fd144 120 return r;
b77c08e0 121
066a6f07 122 bool equal = strchr(c->parameter, '=');
07318c29 123
dd2d3e97 124 STRV_FOREACH(word, args) {
07318c29
LP
125 bool found;
126
07318c29 127 if (equal)
dd2d3e97 128 found = streq(*word, c->parameter);
07318c29
LP
129 else {
130 const char *f;
131
dd2d3e97 132 f = startswith(*word, c->parameter);
4c701096 133 found = f && IN_SET(*f, 0, '=');
b77c08e0
TG
134 }
135
07318c29 136 if (found)
a4705396 137 return true;
b77c08e0 138 }
b77c08e0 139
a4705396 140 return false;
b77c08e0
TG
141}
142
4f80cfca 143static int condition_test_credential(Condition *c, char **env) {
4f80cfca
LP
144 int r;
145
146 assert(c);
147 assert(c->parameter);
148 assert(c->type == CONDITION_CREDENTIAL);
149
e1b45a75 150 /* For now we'll do a very simple existence check and are happy with either a regular or an encrypted
4f80cfca
LP
151 * credential. Given that we check the syntax of the argument we have the option to later maybe allow
152 * contents checks too without breaking compatibility, but for now let's be minimalistic. */
153
154 if (!credential_name_valid(c->parameter)) /* credentials with invalid names do not exist */
155 return false;
156
2d708781
MY
157 int (*gd)(const char **ret);
158 FOREACH_ARGUMENT(gd, get_credentials_dir, get_encrypted_credentials_dir) {
4f80cfca
LP
159 _cleanup_free_ char *j = NULL;
160 const char *cd;
161
162 r = gd(&cd);
163 if (r == -ENXIO) /* no env var set */
164 continue;
165 if (r < 0)
166 return r;
167
168 j = path_join(cd, c->parameter);
169 if (!j)
170 return -ENOMEM;
171
172 if (laccess(j, F_OK) >= 0)
173 return true; /* yay! */
174 if (errno != ENOENT)
175 return -errno;
176
177 /* not found in this dir */
178 }
179
180 return false;
181}
182
a0b191b7 183static int condition_test_kernel_version(Condition *c, char **env) {
a8835c11 184 CompareOperator operator;
ba22ff13 185 struct utsname u;
910c6d09 186 bool first = true;
ba22ff13
LP
187
188 assert(c);
189 assert(c->parameter);
190 assert(c->type == CONDITION_KERNEL_VERSION);
191
192 assert_se(uname(&u) >= 0);
193
066a6f07 194 for (const char *p = c->parameter;;) {
910c6d09
ZJS
195 _cleanup_free_ char *word = NULL;
196 const char *s;
197 int r;
198
199 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
200 if (r < 0)
201 return log_debug_errno(r, "Failed to parse condition string \"%s\": %m", p);
202 if (r == 0)
203 break;
204
205 s = strstrip(word);
06219747 206 operator = parse_compare_operator(&s, COMPARE_ALLOW_FNMATCH|COMPARE_EQUAL_BY_STRING);
c9907425
LP
207 if (operator < 0) /* No prefix? Then treat as glob string */
208 operator = COMPARE_FNMATCH_EQUAL;
209
210 s += strspn(s, WHITESPACE);
211 if (isempty(s)) {
212 if (first) {
213 /* For backwards compatibility, allow whitespace between the operator and
214 * value, without quoting, but only in the first expression. */
215 word = mfree(word);
216 r = extract_first_word(&p, &word, NULL, 0);
217 if (r < 0)
218 return log_debug_errno(r, "Failed to parse condition string \"%s\": %m", p);
219 if (r == 0)
910c6d09 220 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Unexpected end of expression: %s", p);
c9907425
LP
221 s = word;
222 } else
223 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Unexpected end of expression: %s", p);
224 }
910c6d09 225
c9907425
LP
226 r = version_or_fnmatch_compare(operator, u.release, s);
227 if (r < 0)
228 return r;
229 if (!r)
910c6d09
ZJS
230 return false;
231
232 first = false;
233 }
ba22ff13 234
910c6d09 235 return true;
ba22ff13
LP
236}
237
1e26f8a6 238static int condition_test_osrelease(Condition *c, char **env) {
1e26f8a6
LB
239 int r;
240
241 assert(c);
1e26f8a6
LB
242 assert(c->type == CONDITION_OS_RELEASE);
243
066a6f07 244 for (const char *parameter = ASSERT_PTR(c->parameter);;) {
1e26f8a6 245 _cleanup_free_ char *key = NULL, *condition = NULL, *actual_value = NULL;
a8835c11 246 CompareOperator operator;
1e26f8a6 247 const char *word;
1e26f8a6
LB
248
249 r = extract_first_word(&parameter, &condition, NULL, EXTRACT_UNQUOTE);
250 if (r < 0)
251 return log_debug_errno(r, "Failed to parse parameter: %m");
252 if (r == 0)
253 break;
254
a8835c11 255 /* parse_compare_operator() needs the string to start with the comparators */
1e26f8a6 256 word = condition;
4803b0bc 257 r = extract_first_word(&word, &key, COMPARE_OPERATOR_WITH_FNMATCH_CHARS, EXTRACT_RETAIN_SEPARATORS);
1e26f8a6
LB
258 if (r < 0)
259 return log_debug_errno(r, "Failed to parse parameter: %m");
260 /* The os-release spec mandates env-var-like key names */
261 if (r == 0 || isempty(word) || !env_name_is_valid(key))
262 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
4e494e6a 263 "Failed to parse parameter, key/value format expected.");
1e26f8a6
LB
264
265 /* Do not allow whitespace after the separator, as that's not a valid os-release format */
8daa6740 266 operator = parse_compare_operator(&word, COMPARE_ALLOW_FNMATCH|COMPARE_EQUAL_BY_STRING);
a8835c11 267 if (operator < 0 || isempty(word) || strchr(WHITESPACE, *word) != NULL)
1e26f8a6 268 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
4e494e6a 269 "Failed to parse parameter, key/value format expected.");
1e26f8a6
LB
270
271 r = parse_os_release(NULL, key, &actual_value);
272 if (r < 0)
273 return log_debug_errno(r, "Failed to parse os-release: %m");
274
57610982
LP
275 r = version_or_fnmatch_compare(operator, actual_value, word);
276 if (r < 0)
277 return r;
278 if (!r)
1e26f8a6
LB
279 return false;
280 }
281
282 return true;
283}
284
a0b191b7 285static int condition_test_memory(Condition *c, char **env) {
a8835c11 286 CompareOperator operator;
754f719a
LP
287 uint64_t m, k;
288 const char *p;
289 int r;
290
291 assert(c);
292 assert(c->parameter);
293 assert(c->type == CONDITION_MEMORY);
294
295 m = physical_memory();
296
297 p = c->parameter;
69f0a609 298 operator = parse_compare_operator(&p, 0);
a8835c11
LP
299 if (operator < 0)
300 operator = COMPARE_GREATER_OR_EQUAL; /* default to >= check, if nothing is specified. */
754f719a 301
a61473bd 302 r = parse_size(p, 1024, &k);
754f719a 303 if (r < 0)
a61473bd 304 return log_debug_errno(r, "Failed to parse size '%s': %m", p);
754f719a 305
a8835c11 306 return test_order(CMP(m, k), operator);
754f719a
LP
307}
308
a0b191b7 309static int condition_test_cpus(Condition *c, char **env) {
a8835c11 310 CompareOperator operator;
754f719a
LP
311 const char *p;
312 unsigned k;
313 int r, n;
314
315 assert(c);
316 assert(c->parameter);
317 assert(c->type == CONDITION_CPUS);
318
319 n = cpus_in_affinity_mask();
320 if (n < 0)
321 return log_debug_errno(n, "Failed to determine CPUs in affinity mask: %m");
322
323 p = c->parameter;
69f0a609 324 operator = parse_compare_operator(&p, 0);
a8835c11
LP
325 if (operator < 0)
326 operator = COMPARE_GREATER_OR_EQUAL; /* default to >= check, if nothing is specified. */
754f719a
LP
327
328 r = safe_atou(p, &k);
329 if (r < 0)
330 return log_debug_errno(r, "Failed to parse number of CPUs: %m");
331
a8835c11 332 return test_order(CMP((unsigned) n, k), operator);
754f719a
LP
333}
334
a0b191b7 335static int condition_test_user(Condition *c, char **env) {
c465a29f
FS
336 uid_t id;
337 int r;
c465a29f
FS
338
339 assert(c);
340 assert(c->parameter);
341 assert(c->type == CONDITION_USER);
342
734f96b8
ZJS
343 /* Do the quick&easy comparisons first, and only parse the UID later. */
344 if (streq(c->parameter, "root"))
345 return getuid() == 0 || geteuid() == 0;
346 if (streq(c->parameter, NOBODY_USER_NAME))
347 return getuid() == UID_NOBODY || geteuid() == UID_NOBODY;
348 if (streq(c->parameter, "@system"))
349 return uid_is_system(getuid()) || uid_is_system(geteuid());
350
c465a29f
FS
351 r = parse_uid(c->parameter, &id);
352 if (r >= 0)
353 return id == getuid() || id == geteuid();
354
734f96b8
ZJS
355 if (getpid_cached() == 1) /* We already checked for "root" above, and we know that
356 * PID 1 is running as root, hence we know it cannot match. */
357 return false;
534bab66 358
734f96b8
ZJS
359 /* getusername_malloc() may do an nss lookup, which is not allowed in PID 1. */
360 _cleanup_free_ char *username = getusername_malloc();
c465a29f
FS
361 if (!username)
362 return -ENOMEM;
363
364 if (streq(username, c->parameter))
365 return 1;
366
734f96b8 367 const char *u = c->parameter;
fafff8f1 368 r = get_user_creds(&u, &id, NULL, NULL, NULL, USER_CREDS_ALLOW_MISSING);
c465a29f
FS
369 if (r < 0)
370 return 0;
371
372 return id == getuid() || id == geteuid();
373}
374
a0b191b7 375static int condition_test_control_group_controller(Condition *c, char **env) {
e16647c3
CD
376 int r;
377 CGroupMask system_mask, wanted_mask = 0;
378
379 assert(c);
380 assert(c->parameter);
381 assert(c->type == CONDITION_CONTROL_GROUP_CONTROLLER);
382
8ebfd50a
ZJS
383 if (streq(c->parameter, "v2"))
384 return cg_all_unified();
385 if (streq(c->parameter, "v1")) {
386 r = cg_all_unified();
387 if (r < 0)
388 return r;
389 return !r;
390 }
391
e16647c3
CD
392 r = cg_mask_supported(&system_mask);
393 if (r < 0)
394 return log_debug_errno(r, "Failed to determine supported controllers: %m");
395
396 r = cg_mask_from_string(c->parameter, &wanted_mask);
397 if (r < 0 || wanted_mask <= 0) {
398 /* This won't catch the case that we have an unknown controller
399 * mixed in with valid ones -- these are only assessed on the
400 * validity of the valid controllers found. */
401 log_debug("Failed to parse cgroup string: %s", c->parameter);
402 return 1;
403 }
404
d94a24ca 405 return FLAGS_SET(system_mask, wanted_mask);
e16647c3
CD
406}
407
a0b191b7 408static int condition_test_group(Condition *c, char **env) {
c465a29f
FS
409 gid_t id;
410 int r;
411
412 assert(c);
413 assert(c->parameter);
414 assert(c->type == CONDITION_GROUP);
415
416 r = parse_gid(c->parameter, &id);
417 if (r >= 0)
418 return in_gid(id);
419
420 /* Avoid any NSS lookups if we are PID1 */
df0ff127 421 if (getpid_cached() == 1)
c465a29f
FS
422 return streq(c->parameter, "root");
423
424 return in_group(c->parameter) > 0;
425}
426
a0b191b7 427static int condition_test_virtualization(Condition *c, char **env) {
1b86c7c5
LP
428 Virtualization v;
429 int b;
b77c08e0
TG
430
431 assert(c);
432 assert(c->parameter);
433 assert(c->type == CONDITION_VIRTUALIZATION);
434
239a5707
ZJS
435 if (streq(c->parameter, "private-users"))
436 return running_in_userns();
437
75f86906 438 v = detect_virtualization();
592fd144
LP
439 if (v < 0)
440 return v;
b77c08e0
TG
441
442 /* First, compare with yes/no */
443 b = parse_boolean(c->parameter);
0809d774 444 if (b >= 0)
1b86c7c5 445 return b == (v != VIRTUALIZATION_NONE);
b77c08e0
TG
446
447 /* Then, compare categorization */
0809d774
ZJS
448 if (streq(c->parameter, "vm"))
449 return VIRTUALIZATION_IS_VM(v);
b77c08e0 450
0809d774
ZJS
451 if (streq(c->parameter, "container"))
452 return VIRTUALIZATION_IS_CONTAINER(v);
b77c08e0
TG
453
454 /* Finally compare id */
75f86906 455 return v != VIRTUALIZATION_NONE && streq(c->parameter, virtualization_to_string(v));
b77c08e0
TG
456}
457
a0b191b7 458static int condition_test_architecture(Condition *c, char **env) {
6b41a7b2 459 Architecture a, b;
099524d7
LP
460
461 assert(c);
462 assert(c->parameter);
463 assert(c->type == CONDITION_ARCHITECTURE);
464
465 a = uname_architecture();
466 if (a < 0)
592fd144 467 return a;
099524d7
LP
468
469 if (streq(c->parameter, "native"))
470 b = native_architecture();
2cb62395 471 else {
099524d7 472 b = architecture_from_string(c->parameter);
2cb62395
LP
473 if (b < 0) /* unknown architecture? Then it's definitely not ours */
474 return false;
475 }
099524d7 476
a4705396 477 return a == b;
099524d7
LP
478}
479
c6dce572 480#define DTCOMPAT_FILE "/proc/device-tree/compatible"
cbcdcaaa
UKK
481static int condition_test_firmware_devicetree_compatible(const char *dtcarg) {
482 int r;
483 _cleanup_free_ char *dtcompat = NULL;
484 _cleanup_strv_free_ char **dtcompatlist = NULL;
485 size_t size;
486
487 r = read_full_virtual_file(DTCOMPAT_FILE, &dtcompat, &size);
488 if (r < 0) {
489 /* if the path doesn't exist it is incompatible */
490 if (r != -ENOENT)
491 log_debug_errno(r, "Failed to open() '%s', assuming machine is incompatible: %m", DTCOMPAT_FILE);
492 return false;
493 }
494
495 /* Not sure this can happen, but play safe. */
496 if (size == 0) {
497 log_debug("%s has zero length, assuming machine is incompatible", DTCOMPAT_FILE);
498 return false;
499 }
500
c6dce572
YW
501 /* /proc/device-tree/compatible consists of one or more strings, each ending in '\0'.
502 * So the last character in dtcompat must be a '\0'. */
cbcdcaaa
UKK
503 if (dtcompat[size - 1] != '\0') {
504 log_debug("%s is in an unknown format, assuming machine is incompatible", DTCOMPAT_FILE);
505 return false;
506 }
507
508 dtcompatlist = strv_parse_nulstr(dtcompat, size);
509 if (!dtcompatlist)
510 return -ENOMEM;
511
512 return strv_contains(dtcompatlist, dtcarg);
513}
514
bf07a125
DB
515static int condition_test_firmware_smbios_field(const char *expression) {
516 _cleanup_free_ char *field = NULL, *expected_value = NULL, *actual_value = NULL;
a8835c11 517 CompareOperator operator;
bf07a125
DB
518 int r;
519
520 assert(expression);
521
522 /* Parse SMBIOS field */
4803b0bc 523 r = extract_first_word(&expression, &field, COMPARE_OPERATOR_WITH_FNMATCH_CHARS, EXTRACT_RETAIN_SEPARATORS);
bf07a125
DB
524 if (r < 0)
525 return r;
526 if (r == 0 || isempty(expression))
527 return -EINVAL;
528
529 /* Remove trailing spaces from SMBIOS field */
530 delete_trailing_chars(field, WHITESPACE);
531
532 /* Parse operator */
666d314a 533 operator = parse_compare_operator(&expression, COMPARE_ALLOW_FNMATCH|COMPARE_EQUAL_BY_STRING);
bf07a125
DB
534 if (operator < 0)
535 return operator;
536
537 /* Parse expected value */
538 r = extract_first_word(&expression, &expected_value, NULL, EXTRACT_UNQUOTE);
539 if (r < 0)
540 return r;
541 if (r == 0 || !isempty(expression))
542 return -EINVAL;
543
544 /* Read actual value from sysfs */
545 if (!filename_is_valid(field))
4e494e6a 546 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid SMBIOS field name.");
bf07a125
DB
547
548 const char *p = strjoina("/sys/class/dmi/id/", field);
549 r = read_virtual_file(p, SIZE_MAX, &actual_value, NULL);
550 if (r < 0) {
551 log_debug_errno(r, "Failed to read %s: %m", p);
552 if (r == -ENOENT)
553 return false;
554 return r;
555 }
556
557 /* Remove trailing newline */
558 delete_trailing_chars(actual_value, WHITESPACE);
559
560 /* Finally compare actual and expected value */
8bd2cf6e 561 return version_or_fnmatch_compare(operator, actual_value, expected_value);
bf07a125
DB
562}
563
cbcdcaaa 564static int condition_test_firmware(Condition *c, char **env) {
bf07a125
DB
565 sd_char *arg;
566 int r;
cbcdcaaa
UKK
567
568 assert(c);
569 assert(c->parameter);
570 assert(c->type == CONDITION_FIRMWARE);
571
572 if (streq(c->parameter, "device-tree")) {
1037178a 573 if (access("/sys/firmware/devicetree/", F_OK) < 0) {
cbcdcaaa 574 if (errno != ENOENT)
1037178a 575 log_debug_errno(errno, "Unexpected error when checking for /sys/firmware/devicetree/: %m");
cbcdcaaa
UKK
576 return false;
577 } else
578 return true;
bf07a125
DB
579 } else if ((arg = startswith(c->parameter, "device-tree-compatible("))) {
580 _cleanup_free_ char *dtc_arg = NULL;
cbcdcaaa
UKK
581 char *end;
582
1dc042ae 583 end = strrchr(arg, ')');
cbcdcaaa 584 if (!end || *(end + 1) != '\0') {
bf07a125 585 log_debug("Malformed ConditionFirmware=%s", c->parameter);
cbcdcaaa
UKK
586 return false;
587 }
588
bf07a125
DB
589 dtc_arg = strndup(arg, end - arg);
590 if (!dtc_arg)
cbcdcaaa
UKK
591 return -ENOMEM;
592
bf07a125 593 return condition_test_firmware_devicetree_compatible(dtc_arg);
cbcdcaaa
UKK
594 } else if (streq(c->parameter, "uefi"))
595 return is_efi_boot();
bf07a125
DB
596 else if ((arg = startswith(c->parameter, "smbios-field("))) {
597 _cleanup_free_ char *smbios_arg = NULL;
598 char *end;
599
1dc042ae 600 end = strrchr(arg, ')');
bf07a125 601 if (!end || *(end + 1) != '\0')
4e494e6a 602 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Malformed ConditionFirmware=%s.", c->parameter);
bf07a125
DB
603
604 smbios_arg = strndup(arg, end - arg);
605 if (!smbios_arg)
606 return log_oom_debug();
607
608 r = condition_test_firmware_smbios_field(smbios_arg);
609 if (r < 0)
610 return log_debug_errno(r, "Malformed ConditionFirmware=%s: %m", c->parameter);
611 return r;
612 } else {
cbcdcaaa
UKK
613 log_debug("Unsupported Firmware condition \"%s\"", c->parameter);
614 return false;
615 }
616}
617
a0b191b7 618static int condition_test_host(Condition *c, char **env) {
dc92e62c 619 _cleanup_free_ char *h = NULL;
b77c08e0 620 sd_id128_t x, y;
b77c08e0 621 int r;
b77c08e0
TG
622
623 assert(c);
624 assert(c->parameter);
625 assert(c->type == CONDITION_HOST);
626
627 if (sd_id128_from_string(c->parameter, &x) >= 0) {
628
629 r = sd_id128_get_machine(&y);
630 if (r < 0)
592fd144 631 return r;
b77c08e0 632
a4705396 633 return sd_id128_equal(x, y);
b77c08e0
TG
634 }
635
636 h = gethostname_malloc();
637 if (!h)
592fd144 638 return -ENOMEM;
b77c08e0 639
7fd0fb02
LP
640 r = fnmatch(c->parameter, h, FNM_CASEFOLD);
641 if (r == FNM_NOMATCH)
642 return false;
643 if (r != 0)
644 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "fnmatch() failed.");
645
646 return true;
b77c08e0
TG
647}
648
a0b191b7 649static int condition_test_ac_power(Condition *c, char **env) {
b77c08e0
TG
650 int r;
651
652 assert(c);
653 assert(c->parameter);
654 assert(c->type == CONDITION_AC_POWER);
655
656 r = parse_boolean(c->parameter);
657 if (r < 0)
592fd144 658 return r;
b77c08e0 659
a4705396 660 return (on_ac_power() != 0) == !!r;
b77c08e0
TG
661}
662
bce334a3 663static int has_tpm2(void) {
300bba79
DDM
664 /* Checks whether the kernel has the TPM subsystem enabled and the firmware reports support. Note
665 * we don't check for actual TPM devices, since we might not have loaded the driver for it yet, i.e.
666 * during early boot where we very likely want to use this condition check).
e1be2c77
LP
667 *
668 * Note that we don't check if we ourselves are built with TPM2 support here! */
bce334a3 669
300bba79 670 return FLAGS_SET(tpm2_support(), TPM2_SUPPORT_SUBSYSTEM|TPM2_SUPPORT_FIRMWARE);
bce334a3
LP
671}
672
a0b191b7 673static int condition_test_security(Condition *c, char **env) {
d1bddcec
LP
674 assert(c);
675 assert(c->parameter);
676 assert(c->type == CONDITION_SECURITY);
677
678 if (streq(c->parameter, "selinux"))
6d395665 679 return mac_selinux_use();
d1bddcec 680 if (streq(c->parameter, "smack"))
a4705396 681 return mac_smack_use();
d1bddcec 682 if (streq(c->parameter, "apparmor"))
a4705396 683 return mac_apparmor_use();
d1bddcec 684 if (streq(c->parameter, "audit"))
a4705396 685 return use_audit();
d1bddcec 686 if (streq(c->parameter, "ima"))
a4705396 687 return use_ima();
ed440f6b
SL
688 if (streq(c->parameter, "tomoyo"))
689 return mac_tomoyo_use();
be405b90
LP
690 if (streq(c->parameter, "uefi-secureboot"))
691 return is_efi_secure_boot();
bce334a3
LP
692 if (streq(c->parameter, "tpm2"))
693 return has_tpm2();
95d043b1
DB
694 if (streq(c->parameter, "cvm"))
695 return detect_confidential_virtualization() > 0;
340d979a
LP
696 if (streq(c->parameter, "measured-uki"))
697 return efi_measured_uki(LOG_DEBUG);
d1bddcec 698
a4705396 699 return false;
d1bddcec
LP
700}
701
a0b191b7 702static int condition_test_capability(Condition *c, char **env) {
9c6f9786 703 unsigned long long capabilities = (unsigned long long) -1;
d1bddcec 704 _cleanup_fclose_ FILE *f = NULL;
9c6f9786 705 int value, r;
d1bddcec
LP
706
707 assert(c);
708 assert(c->parameter);
709 assert(c->type == CONDITION_CAPABILITY);
710
711 /* If it's an invalid capability, we don't have it */
2822da4f
LP
712 value = capability_from_name(c->parameter);
713 if (value < 0)
d1bddcec
LP
714 return -EINVAL;
715
716 /* If it's a valid capability we default to assume
717 * that we have it */
718
719 f = fopen("/proc/self/status", "re");
720 if (!f)
721 return -errno;
722
9c6f9786
LP
723 for (;;) {
724 _cleanup_free_ char *line = NULL;
9c6f9786
LP
725
726 r = read_line(f, LONG_LINE_MAX, &line);
727 if (r < 0)
728 return r;
729 if (r == 0)
730 break;
731
066a6f07 732 const char *p = startswith(line, "CapBnd:");
9c6f9786 733 if (p) {
066a6f07 734 if (sscanf(p, "%llx", &capabilities) != 1)
9c6f9786 735 return -EIO;
d1bddcec 736
d1bddcec
LP
737 break;
738 }
739 }
740
a4705396 741 return !!(capabilities & (1ULL << value));
d1bddcec
LP
742}
743
a0b191b7 744static int condition_test_needs_update(Condition *c, char **env) {
d1bddcec 745 struct stat usr, other;
df1f5dc1 746 const char *p;
f8b4ae29 747 bool b;
df1f5dc1 748 int r;
d1bddcec
LP
749
750 assert(c);
751 assert(c->parameter);
752 assert(c->type == CONDITION_NEEDS_UPDATE);
753
78266a54 754 r = proc_cmdline_get_bool("systemd.condition_needs_update", /* flags = */ 0, &b);
f8b4ae29 755 if (r < 0)
78266a54 756 log_debug_errno(r, "Failed to parse systemd.condition_needs_update= kernel command line argument, ignoring: %m");
f8b4ae29
LP
757 if (r > 0)
758 return b;
759
294eace0
ZJS
760 if (in_initrd()) {
761 log_debug("We are in an initrd, not doing any updates.");
762 return false;
763 }
764
df1f5dc1
LP
765 if (!path_is_absolute(c->parameter)) {
766 log_debug("Specified condition parameter '%s' is not absolute, assuming an update is needed.", c->parameter);
841c0987 767 return true;
df1f5dc1 768 }
841c0987 769
d1bddcec 770 /* If the file system is read-only we shouldn't suggest an update */
df1f5dc1
LP
771 r = path_is_read_only_fs(c->parameter);
772 if (r < 0)
773 log_debug_errno(r, "Failed to determine if '%s' is read-only, ignoring: %m", c->parameter);
774 if (r > 0)
a4705396 775 return false;
d1bddcec 776
841c0987
LP
777 /* Any other failure means we should allow the condition to be true, so that we rather invoke too
778 * many update tools than too few. */
d1bddcec 779
63c372cb 780 p = strjoina(c->parameter, "/.updated");
df1f5dc1
LP
781 if (lstat(p, &other) < 0) {
782 if (errno != ENOENT)
783 log_debug_errno(errno, "Failed to stat() '%s', assuming an update is needed: %m", p);
a4705396 784 return true;
df1f5dc1 785 }
d1bddcec 786
df1f5dc1
LP
787 if (lstat("/usr/", &usr) < 0) {
788 log_debug_errno(errno, "Failed to stat() /usr/, assuming an update is needed: %m");
a4705396 789 return true;
df1f5dc1 790 }
d1bddcec 791
fb8b0869
IS
792 /*
793 * First, compare seconds as they are always accurate...
794 */
795 if (usr.st_mtim.tv_sec != other.st_mtim.tv_sec)
796 return usr.st_mtim.tv_sec > other.st_mtim.tv_sec;
797
798 /*
799 * ...then compare nanoseconds.
800 *
801 * A false positive is only possible when /usr's nanoseconds > 0
802 * (otherwise /usr cannot be strictly newer than the target file)
803 * AND the target file's nanoseconds == 0
804 * (otherwise the filesystem supports nsec timestamps, see stat(2)).
805 */
ce0f7f55
LP
806 if (usr.st_mtim.tv_nsec == 0 || other.st_mtim.tv_nsec > 0)
807 return usr.st_mtim.tv_nsec > other.st_mtim.tv_nsec;
fb8b0869 808
ce0f7f55
LP
809 _cleanup_free_ char *timestamp_str = NULL;
810 r = parse_env_file(NULL, p, "TIMESTAMP_NSEC", &timestamp_str);
811 if (r < 0) {
812 log_debug_errno(r, "Failed to parse timestamp file '%s', using mtime: %m", p);
813 return true;
99aad9a2
ZJS
814 }
815 if (isempty(timestamp_str)) {
ce0f7f55
LP
816 log_debug("No data in timestamp file '%s', using mtime.", p);
817 return true;
818 }
fb8b0869 819
ce0f7f55
LP
820 uint64_t timestamp;
821 r = safe_atou64(timestamp_str, &timestamp);
822 if (r < 0) {
823 log_debug_errno(r, "Failed to parse timestamp value '%s' in file '%s', using mtime: %m", timestamp_str, p);
824 return true;
fb8b0869
IS
825 }
826
ce0f7f55 827 return timespec_load_nsec(&usr.st_mtim) > timestamp;
d1bddcec
LP
828}
829
bd3beda2
ZJS
830static bool in_first_boot(void) {
831 static int first_boot = -1;
832 int r;
833
834 if (first_boot >= 0)
835 return first_boot;
836
837 const char *e = secure_getenv("SYSTEMD_FIRST_BOOT");
838 if (e) {
839 r = parse_boolean(e);
840 if (r < 0)
841 log_debug_errno(r, "Failed to parse $SYSTEMD_FIRST_BOOT, ignoring: %m");
842 else
843 return (first_boot = r);
844 }
845
846 r = RET_NERRNO(access("/run/systemd/first-boot", F_OK));
847 if (r < 0 && r != -ENOENT)
848 log_debug_errno(r, "Failed to check if /run/systemd/first-boot exists, assuming no: %m");
849 return r >= 0;
850}
851
a0b191b7 852static int condition_test_first_boot(Condition *c, char **env) {
bd3beda2 853 int r;
d1bddcec
LP
854
855 assert(c);
856 assert(c->parameter);
857 assert(c->type == CONDITION_FIRST_BOOT);
858
bd3beda2
ZJS
859 // TODO: Parse c->parameter immediately when reading the config.
860 // Apply negation when parsing too.
861
d1bddcec
LP
862 r = parse_boolean(c->parameter);
863 if (r < 0)
864 return r;
865
bd3beda2 866 return in_first_boot() == r;
d1bddcec
LP
867}
868
a0b191b7
LP
869static int condition_test_environment(Condition *c, char **env) {
870 bool equal;
a0b191b7
LP
871
872 assert(c);
873 assert(c->parameter);
874 assert(c->type == CONDITION_ENVIRONMENT);
875
876 equal = strchr(c->parameter, '=');
877
878 STRV_FOREACH(i, env) {
879 bool found;
880
881 if (equal)
882 found = streq(c->parameter, *i);
883 else {
884 const char *f;
885
886 f = startswith(*i, c->parameter);
887 found = f && IN_SET(*f, 0, '=');
888 }
889
890 if (found)
891 return true;
892 }
893
894 return false;
895}
896
897static int condition_test_path_exists(Condition *c, char **env) {
d1bddcec
LP
898 assert(c);
899 assert(c->parameter);
900 assert(c->type == CONDITION_PATH_EXISTS);
901
a4705396 902 return access(c->parameter, F_OK) >= 0;
d1bddcec
LP
903}
904
a0b191b7 905static int condition_test_path_exists_glob(Condition *c, char **env) {
d1bddcec
LP
906 assert(c);
907 assert(c->parameter);
908 assert(c->type == CONDITION_PATH_EXISTS_GLOB);
909
a4705396 910 return glob_exists(c->parameter) > 0;
d1bddcec
LP
911}
912
a0b191b7 913static int condition_test_path_is_directory(Condition *c, char **env) {
d1bddcec
LP
914 assert(c);
915 assert(c->parameter);
916 assert(c->type == CONDITION_PATH_IS_DIRECTORY);
917
a4705396 918 return is_dir(c->parameter, true) > 0;
d1bddcec
LP
919}
920
a0b191b7 921static int condition_test_path_is_symbolic_link(Condition *c, char **env) {
d1bddcec
LP
922 assert(c);
923 assert(c->parameter);
924 assert(c->type == CONDITION_PATH_IS_SYMBOLIC_LINK);
925
a4705396 926 return is_symlink(c->parameter) > 0;
d1bddcec
LP
927}
928
a0b191b7 929static int condition_test_path_is_mount_point(Condition *c, char **env) {
d1bddcec
LP
930 assert(c);
931 assert(c->parameter);
932 assert(c->type == CONDITION_PATH_IS_MOUNT_POINT);
933
b409aacb 934 return path_is_mount_point_full(c->parameter, /* root = */ NULL, AT_SYMLINK_FOLLOW) > 0;
d1bddcec
LP
935}
936
a0b191b7 937static int condition_test_path_is_read_write(Condition *c, char **env) {
7c4c9948 938 int r;
939
d1bddcec
LP
940 assert(c);
941 assert(c->parameter);
942 assert(c->type == CONDITION_PATH_IS_READ_WRITE);
943
7c4c9948 944 r = path_is_read_only_fs(c->parameter);
945
946 return r <= 0 && r != -ENOENT;
d1bddcec
LP
947}
948
68337e55
GS
949static int condition_test_cpufeature(Condition *c, char **env) {
950 assert(c);
951 assert(c->parameter);
952 assert(c->type == CONDITION_CPU_FEATURE);
953
954 return has_cpu_with_flag(ascii_strlower(c->parameter));
955}
956
a0b191b7 957static int condition_test_path_is_encrypted(Condition *c, char **env) {
7f19247b
LP
958 int r;
959
960 assert(c);
961 assert(c->parameter);
962 assert(c->type == CONDITION_PATH_IS_ENCRYPTED);
963
964 r = path_is_encrypted(c->parameter);
965 if (r < 0 && r != -ENOENT)
966 log_debug_errno(r, "Failed to determine if '%s' is encrypted: %m", c->parameter);
967
968 return r > 0;
969}
970
a0b191b7 971static int condition_test_directory_not_empty(Condition *c, char **env) {
d1bddcec
LP
972 int r;
973
974 assert(c);
975 assert(c->parameter);
976 assert(c->type == CONDITION_DIRECTORY_NOT_EMPTY);
977
db55bbf2 978 r = dir_is_empty(c->parameter, /* ignore_hidden_or_backup= */ true);
193105f2 979 return r <= 0 && !IN_SET(r, -ENOENT, -ENOTDIR);
d1bddcec
LP
980}
981
a0b191b7 982static int condition_test_file_not_empty(Condition *c, char **env) {
d1bddcec
LP
983 struct stat st;
984
985 assert(c);
986 assert(c->parameter);
987 assert(c->type == CONDITION_FILE_NOT_EMPTY);
988
989 return (stat(c->parameter, &st) >= 0 &&
990 S_ISREG(st.st_mode) &&
a4705396 991 st.st_size > 0);
d1bddcec
LP
992}
993
a0b191b7 994static int condition_test_file_is_executable(Condition *c, char **env) {
d1bddcec
LP
995 struct stat st;
996
997 assert(c);
998 assert(c->parameter);
999 assert(c->type == CONDITION_FILE_IS_EXECUTABLE);
1000
1001 return (stat(c->parameter, &st) >= 0 &&
1002 S_ISREG(st.st_mode) &&
a4705396 1003 (st.st_mode & 0111));
d1bddcec
LP
1004}
1005
81513b38
LB
1006static int condition_test_psi(Condition *c, char **env) {
1007 _cleanup_free_ char *first = NULL, *second = NULL, *third = NULL, *fourth = NULL, *pressure_path = NULL;
1008 const char *p, *value, *pressure_type;
1009 loadavg_t *current, limit;
1010 ResourcePressure pressure;
1011 int r;
1012
1013 assert(c);
1014 assert(c->parameter);
1015 assert(IN_SET(c->type, CONDITION_MEMORY_PRESSURE, CONDITION_CPU_PRESSURE, CONDITION_IO_PRESSURE));
1016
1017 if (!is_pressure_supported()) {
1018 log_debug("Pressure Stall Information (PSI) is not supported, skipping.");
1019 return 1;
1020 }
1021
1022 pressure_type = c->type == CONDITION_MEMORY_PRESSURE ? "memory" :
1023 c->type == CONDITION_CPU_PRESSURE ? "cpu" :
1024 "io";
1025
1026 p = c->parameter;
4f495126 1027 r = extract_many_words(&p, ":", 0, &first, &second);
81513b38
LB
1028 if (r <= 0)
1029 return log_debug_errno(r < 0 ? r : SYNTHETIC_ERRNO(EINVAL), "Failed to parse condition parameter %s: %m", c->parameter);
1030 /* If only one parameter is passed, then we look at the global system pressure rather than a specific cgroup. */
1031 if (r == 1) {
1032 pressure_path = path_join("/proc/pressure", pressure_type);
1033 if (!pressure_path)
68ee5d77 1034 return log_oom_debug();
81513b38
LB
1035
1036 value = first;
1037 } else {
1038 const char *controller = strjoina(pressure_type, ".pressure");
68ee5d77 1039 _cleanup_free_ char *slice_path = NULL, *root_scope = NULL;
81513b38 1040 CGroupMask mask, required_mask;
68ee5d77 1041 char *slice, *e;
81513b38
LB
1042
1043 required_mask = c->type == CONDITION_MEMORY_PRESSURE ? CGROUP_MASK_MEMORY :
1044 c->type == CONDITION_CPU_PRESSURE ? CGROUP_MASK_CPU :
1045 CGROUP_MASK_IO;
1046
1047 slice = strstrip(first);
1048 if (!slice)
4e494e6a 1049 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to parse condition parameter %s.", c->parameter);
81513b38
LB
1050
1051 r = cg_all_unified();
1052 if (r < 0)
1053 return log_debug_errno(r, "Failed to determine whether the unified cgroups hierarchy is used: %m");
1054 if (r == 0) {
1055 log_debug("PSI condition check requires the unified cgroups hierarchy, skipping.");
1056 return 1;
1057 }
1058
1059 r = cg_mask_supported(&mask);
1060 if (r < 0)
1061 return log_debug_errno(r, "Failed to get supported cgroup controllers: %m");
1062
1063 if (!FLAGS_SET(mask, required_mask)) {
1064 log_debug("Cgroup %s controller not available, skipping PSI condition check.", pressure_type);
1065 return 1;
1066 }
1067
1068 r = cg_slice_to_path(slice, &slice_path);
1069 if (r < 0)
1070 return log_debug_errno(r, "Cannot determine slice \"%s\" cgroup path: %m", slice);
1071
68ee5d77
LB
1072 /* We might be running under the user manager, so get the root path and prefix it accordingly. */
1073 r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, getpid_cached(), &root_scope);
1074 if (r < 0)
1075 return log_debug_errno(r, "Failed to get root cgroup path: %m");
1076
1077 /* Drop init.scope, we want the parent. We could get an empty or / path, but that's fine,
1078 * just skip it in that case. */
1079 e = endswith(root_scope, "/" SPECIAL_INIT_SCOPE);
1080 if (e)
1081 *e = 0;
1082 if (!empty_or_root(root_scope)) {
1083 _cleanup_free_ char *slice_joined = NULL;
1084
1085 slice_joined = path_join(root_scope, slice_path);
1086 if (!slice_joined)
1087 return log_oom_debug();
1088
1089 free_and_replace(slice_path, slice_joined);
1090 }
1091
81513b38
LB
1092 r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, slice_path, controller, &pressure_path);
1093 if (r < 0)
1094 return log_debug_errno(r, "Error getting cgroup pressure path from %s: %m", slice_path);
1095
1096 value = second;
1097 }
1098
1099 /* If a value including a specific timespan (in the intervals allowed by the kernel),
1100 * parse it, otherwise we assume just a plain percentage that will be checked if it is
1101 * smaller or equal to the current pressure average over 5 minutes. */
4f495126 1102 r = extract_many_words(&value, "/", 0, &third, &fourth);
81513b38
LB
1103 if (r <= 0)
1104 return log_debug_errno(r < 0 ? r : SYNTHETIC_ERRNO(EINVAL), "Failed to parse condition parameter %s: %m", c->parameter);
1105 if (r == 1)
1106 current = &pressure.avg300;
1107 else {
1108 const char *timespan;
1109
1110 timespan = skip_leading_chars(fourth, NULL);
1111 if (!timespan)
4e494e6a 1112 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to parse condition parameter %s.", c->parameter);
81513b38
LB
1113
1114 if (startswith(timespan, "10sec"))
1115 current = &pressure.avg10;
1116 else if (startswith(timespan, "1min"))
1117 current = &pressure.avg60;
1118 else if (startswith(timespan, "5min"))
1119 current = &pressure.avg300;
1120 else
4e494e6a 1121 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to parse condition parameter %s.", c->parameter);
81513b38
LB
1122 }
1123
1124 value = strstrip(third);
1125 if (!value)
4e494e6a 1126 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to parse condition parameter %s.", c->parameter);
81513b38
LB
1127
1128 r = parse_permyriad(value);
1129 if (r < 0)
1130 return log_debug_errno(r, "Failed to parse permyriad: %s", c->parameter);
1131
1132 r = store_loadavg_fixed_point(r / 100LU, r % 100LU, &limit);
1133 if (r < 0)
1134 return log_debug_errno(r, "Failed to parse loadavg: %s", c->parameter);
1135
1136 r = read_resource_pressure(pressure_path, PRESSURE_TYPE_FULL, &pressure);
1137 if (r == -ENODATA) /* cpu.pressure 'full' was added recently, fall back to 'some'. */
1138 r = read_resource_pressure(pressure_path, PRESSURE_TYPE_SOME, &pressure);
1139 if (r == -ENOENT) {
1140 /* We already checked that /proc/pressure exists, so this means we were given a cgroup
1141 * that doesn't exist or doesn't exist any longer. */
1142 log_debug("\"%s\" not found, skipping PSI check.", pressure_path);
1143 return 1;
1144 }
1145 if (r < 0)
1146 return log_debug_errno(r, "Error parsing pressure from %s: %m", pressure_path);
1147
1148 return *current <= limit;
1149}
1150
a0b191b7 1151int condition_test(Condition *c, char **env) {
d1bddcec 1152
a0b191b7 1153 static int (*const condition_tests[_CONDITION_TYPE_MAX])(Condition *c, char **env) = {
cae90de3
ZJS
1154 [CONDITION_PATH_EXISTS] = condition_test_path_exists,
1155 [CONDITION_PATH_EXISTS_GLOB] = condition_test_path_exists_glob,
1156 [CONDITION_PATH_IS_DIRECTORY] = condition_test_path_is_directory,
1157 [CONDITION_PATH_IS_SYMBOLIC_LINK] = condition_test_path_is_symbolic_link,
1158 [CONDITION_PATH_IS_MOUNT_POINT] = condition_test_path_is_mount_point,
1159 [CONDITION_PATH_IS_READ_WRITE] = condition_test_path_is_read_write,
7f19247b 1160 [CONDITION_PATH_IS_ENCRYPTED] = condition_test_path_is_encrypted,
cae90de3
ZJS
1161 [CONDITION_DIRECTORY_NOT_EMPTY] = condition_test_directory_not_empty,
1162 [CONDITION_FILE_NOT_EMPTY] = condition_test_file_not_empty,
1163 [CONDITION_FILE_IS_EXECUTABLE] = condition_test_file_is_executable,
1164 [CONDITION_KERNEL_COMMAND_LINE] = condition_test_kernel_command_line,
1165 [CONDITION_KERNEL_VERSION] = condition_test_kernel_version,
4f80cfca 1166 [CONDITION_CREDENTIAL] = condition_test_credential,
cae90de3
ZJS
1167 [CONDITION_VIRTUALIZATION] = condition_test_virtualization,
1168 [CONDITION_SECURITY] = condition_test_security,
1169 [CONDITION_CAPABILITY] = condition_test_capability,
1170 [CONDITION_HOST] = condition_test_host,
1171 [CONDITION_AC_POWER] = condition_test_ac_power,
1172 [CONDITION_ARCHITECTURE] = condition_test_architecture,
cbcdcaaa 1173 [CONDITION_FIRMWARE] = condition_test_firmware,
cae90de3
ZJS
1174 [CONDITION_NEEDS_UPDATE] = condition_test_needs_update,
1175 [CONDITION_FIRST_BOOT] = condition_test_first_boot,
1176 [CONDITION_USER] = condition_test_user,
1177 [CONDITION_GROUP] = condition_test_group,
e16647c3 1178 [CONDITION_CONTROL_GROUP_CONTROLLER] = condition_test_control_group_controller,
cae90de3
ZJS
1179 [CONDITION_CPUS] = condition_test_cpus,
1180 [CONDITION_MEMORY] = condition_test_memory,
a0b191b7 1181 [CONDITION_ENVIRONMENT] = condition_test_environment,
68337e55 1182 [CONDITION_CPU_FEATURE] = condition_test_cpufeature,
1e26f8a6 1183 [CONDITION_OS_RELEASE] = condition_test_osrelease,
81513b38
LB
1184 [CONDITION_MEMORY_PRESSURE] = condition_test_psi,
1185 [CONDITION_CPU_PRESSURE] = condition_test_psi,
1186 [CONDITION_IO_PRESSURE] = condition_test_psi,
d1bddcec 1187 };
cc50ef13
LP
1188
1189 int r, b;
d1bddcec
LP
1190
1191 assert(c);
1192 assert(c->type >= 0);
1193 assert(c->type < _CONDITION_TYPE_MAX);
1194
a0b191b7 1195 r = condition_tests[c->type](c, env);
cc50ef13
LP
1196 if (r < 0) {
1197 c->result = CONDITION_ERROR;
a4705396 1198 return r;
cc50ef13 1199 }
a4705396 1200
cc50ef13
LP
1201 b = (r > 0) == !c->negate;
1202 c->result = b ? CONDITION_SUCCEEDED : CONDITION_FAILED;
1203 return b;
d1bddcec
LP
1204}
1205
dce719f6
LP
1206bool condition_test_list(
1207 Condition *first,
a0b191b7 1208 char **env,
dce719f6
LP
1209 condition_to_string_t to_string,
1210 condition_test_logger_t logger,
1211 void *userdata) {
1212
828fa610
YW
1213 int triggered = -1;
1214
828fa610
YW
1215 /* If the condition list is empty, then it is true */
1216 if (!first)
1217 return true;
1218
1219 /* Otherwise, if all of the non-trigger conditions apply and
1220 * if any of the trigger conditions apply (unless there are
1221 * none) we return true */
1222 LIST_FOREACH(conditions, c, first) {
1223 int r;
1224
a0b191b7 1225 r = condition_test(c, env);
828fa610
YW
1226
1227 if (logger) {
1228 if (r < 0)
62c6bbbc 1229 logger(userdata, LOG_WARNING, r, PROJECT_FILE, __LINE__, __func__,
828fa610
YW
1230 "Couldn't determine result for %s=%s%s%s, assuming failed: %m",
1231 to_string(c->type),
1232 c->trigger ? "|" : "",
1233 c->negate ? "!" : "",
476cfe62 1234 c->parameter);
828fa610 1235 else
62c6bbbc 1236 logger(userdata, LOG_DEBUG, 0, PROJECT_FILE, __LINE__, __func__,
828fa610
YW
1237 "%s=%s%s%s %s.",
1238 to_string(c->type),
1239 c->trigger ? "|" : "",
1240 c->negate ? "!" : "",
476cfe62 1241 c->parameter,
828fa610
YW
1242 condition_result_to_string(c->result));
1243 }
1244
1245 if (!c->trigger && r <= 0)
1246 return false;
1247
1248 if (c->trigger && triggered <= 0)
1249 triggered = r > 0;
1250 }
1251
1252 return triggered != 0;
1253}
1254
dce719f6 1255void condition_dump(Condition *c, FILE *f, const char *prefix, condition_to_string_t to_string) {
b77c08e0
TG
1256 assert(c);
1257 assert(f);
dce719f6 1258 assert(to_string);
b77c08e0 1259
ad5d4b17 1260 prefix = strempty(prefix);
b77c08e0
TG
1261
1262 fprintf(f,
1263 "%s\t%s: %s%s%s %s\n",
1264 prefix,
59fccdc5 1265 to_string(c->type),
b77c08e0
TG
1266 c->trigger ? "|" : "",
1267 c->negate ? "!" : "",
1268 c->parameter,
cc50ef13 1269 condition_result_to_string(c->result));
b77c08e0
TG
1270}
1271
dce719f6 1272void condition_dump_list(Condition *first, FILE *f, const char *prefix, condition_to_string_t to_string) {
b77c08e0 1273 LIST_FOREACH(conditions, c, first)
59fccdc5 1274 condition_dump(c, f, prefix, to_string);
b77c08e0
TG
1275}
1276
1277static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
651c3318 1278 [CONDITION_ARCHITECTURE] = "ConditionArchitecture",
cbcdcaaa 1279 [CONDITION_FIRMWARE] = "ConditionFirmware",
651c3318
LP
1280 [CONDITION_VIRTUALIZATION] = "ConditionVirtualization",
1281 [CONDITION_HOST] = "ConditionHost",
1282 [CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine",
5022f08a 1283 [CONDITION_KERNEL_VERSION] = "ConditionKernelVersion",
4f80cfca 1284 [CONDITION_CREDENTIAL] = "ConditionCredential",
651c3318
LP
1285 [CONDITION_SECURITY] = "ConditionSecurity",
1286 [CONDITION_CAPABILITY] = "ConditionCapability",
1287 [CONDITION_AC_POWER] = "ConditionACPower",
1288 [CONDITION_NEEDS_UPDATE] = "ConditionNeedsUpdate",
1289 [CONDITION_FIRST_BOOT] = "ConditionFirstBoot",
b77c08e0
TG
1290 [CONDITION_PATH_EXISTS] = "ConditionPathExists",
1291 [CONDITION_PATH_EXISTS_GLOB] = "ConditionPathExistsGlob",
1292 [CONDITION_PATH_IS_DIRECTORY] = "ConditionPathIsDirectory",
1293 [CONDITION_PATH_IS_SYMBOLIC_LINK] = "ConditionPathIsSymbolicLink",
1294 [CONDITION_PATH_IS_MOUNT_POINT] = "ConditionPathIsMountPoint",
1295 [CONDITION_PATH_IS_READ_WRITE] = "ConditionPathIsReadWrite",
7f19247b 1296 [CONDITION_PATH_IS_ENCRYPTED] = "ConditionPathIsEncrypted",
b77c08e0
TG
1297 [CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty",
1298 [CONDITION_FILE_NOT_EMPTY] = "ConditionFileNotEmpty",
1299 [CONDITION_FILE_IS_EXECUTABLE] = "ConditionFileIsExecutable",
c465a29f
FS
1300 [CONDITION_USER] = "ConditionUser",
1301 [CONDITION_GROUP] = "ConditionGroup",
e16647c3 1302 [CONDITION_CONTROL_GROUP_CONTROLLER] = "ConditionControlGroupController",
754f719a
LP
1303 [CONDITION_CPUS] = "ConditionCPUs",
1304 [CONDITION_MEMORY] = "ConditionMemory",
a0b191b7 1305 [CONDITION_ENVIRONMENT] = "ConditionEnvironment",
68337e55 1306 [CONDITION_CPU_FEATURE] = "ConditionCPUFeature",
1e26f8a6 1307 [CONDITION_OS_RELEASE] = "ConditionOSRelease",
81513b38
LB
1308 [CONDITION_MEMORY_PRESSURE] = "ConditionMemoryPressure",
1309 [CONDITION_CPU_PRESSURE] = "ConditionCPUPressure",
1310 [CONDITION_IO_PRESSURE] = "ConditionIOPressure",
b77c08e0
TG
1311};
1312
1313DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType);
cc50ef13 1314
59fccdc5 1315static const char* const assert_type_table[_CONDITION_TYPE_MAX] = {
651c3318 1316 [CONDITION_ARCHITECTURE] = "AssertArchitecture",
cbcdcaaa 1317 [CONDITION_FIRMWARE] = "AssertFirmware",
651c3318
LP
1318 [CONDITION_VIRTUALIZATION] = "AssertVirtualization",
1319 [CONDITION_HOST] = "AssertHost",
1320 [CONDITION_KERNEL_COMMAND_LINE] = "AssertKernelCommandLine",
5022f08a 1321 [CONDITION_KERNEL_VERSION] = "AssertKernelVersion",
4f80cfca 1322 [CONDITION_CREDENTIAL] = "AssertCredential",
651c3318
LP
1323 [CONDITION_SECURITY] = "AssertSecurity",
1324 [CONDITION_CAPABILITY] = "AssertCapability",
1325 [CONDITION_AC_POWER] = "AssertACPower",
1326 [CONDITION_NEEDS_UPDATE] = "AssertNeedsUpdate",
1327 [CONDITION_FIRST_BOOT] = "AssertFirstBoot",
59fccdc5
LP
1328 [CONDITION_PATH_EXISTS] = "AssertPathExists",
1329 [CONDITION_PATH_EXISTS_GLOB] = "AssertPathExistsGlob",
1330 [CONDITION_PATH_IS_DIRECTORY] = "AssertPathIsDirectory",
1331 [CONDITION_PATH_IS_SYMBOLIC_LINK] = "AssertPathIsSymbolicLink",
1332 [CONDITION_PATH_IS_MOUNT_POINT] = "AssertPathIsMountPoint",
1333 [CONDITION_PATH_IS_READ_WRITE] = "AssertPathIsReadWrite",
7f19247b 1334 [CONDITION_PATH_IS_ENCRYPTED] = "AssertPathIsEncrypted",
59fccdc5
LP
1335 [CONDITION_DIRECTORY_NOT_EMPTY] = "AssertDirectoryNotEmpty",
1336 [CONDITION_FILE_NOT_EMPTY] = "AssertFileNotEmpty",
1337 [CONDITION_FILE_IS_EXECUTABLE] = "AssertFileIsExecutable",
c465a29f
FS
1338 [CONDITION_USER] = "AssertUser",
1339 [CONDITION_GROUP] = "AssertGroup",
e16647c3 1340 [CONDITION_CONTROL_GROUP_CONTROLLER] = "AssertControlGroupController",
754f719a
LP
1341 [CONDITION_CPUS] = "AssertCPUs",
1342 [CONDITION_MEMORY] = "AssertMemory",
a0b191b7 1343 [CONDITION_ENVIRONMENT] = "AssertEnvironment",
68337e55 1344 [CONDITION_CPU_FEATURE] = "AssertCPUFeature",
1e26f8a6 1345 [CONDITION_OS_RELEASE] = "AssertOSRelease",
81513b38
LB
1346 [CONDITION_MEMORY_PRESSURE] = "AssertMemoryPressure",
1347 [CONDITION_CPU_PRESSURE] = "AssertCPUPressure",
1348 [CONDITION_IO_PRESSURE] = "AssertIOPressure",
59fccdc5
LP
1349};
1350
1351DEFINE_STRING_TABLE_LOOKUP(assert_type, ConditionType);
1352
cc50ef13
LP
1353static const char* const condition_result_table[_CONDITION_RESULT_MAX] = {
1354 [CONDITION_UNTESTED] = "untested",
1355 [CONDITION_SUCCEEDED] = "succeeded",
1356 [CONDITION_FAILED] = "failed",
1357 [CONDITION_ERROR] = "error",
1358};
1359
1360DEFINE_STRING_TABLE_LOOKUP(condition_result, ConditionResult);