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