]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/condition.c
analyze: fix typo
[thirdparty/systemd.git] / src / shared / condition.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <fcntl.h>
4 #include <fnmatch.h>
5 #include <gnu/libc-version.h>
6 #include <sys/stat.h>
7 #include <sys/utsname.h>
8 #include <time.h>
9 #include <unistd.h>
10
11 #include "sd-id128.h"
12
13 #include "alloc-util.h"
14 #include "apparmor-util.h"
15 #include "architecture.h"
16 #include "battery-util.h"
17 #include "bitfield.h"
18 #include "blockdev-util.h"
19 #include "cap-list.h"
20 #include "capability-util.h"
21 #include "cgroup-util.h"
22 #include "compare-operator.h"
23 #include "condition.h"
24 #include "confidential-virt.h"
25 #include "cpu-set-util.h"
26 #include "creds-util.h"
27 #include "efi-loader.h"
28 #include "efivars.h"
29 #include "env-file.h"
30 #include "env-util.h"
31 #include "extract-word.h"
32 #include "fd-util.h"
33 #include "fileio.h"
34 #include "fs-util.h"
35 #include "glob-util.h"
36 #include "hostname-setup.h"
37 #include "id128-util.h"
38 #include "ima-util.h"
39 #include "initrd-util.h"
40 #include "libaudit-util.h"
41 #include "limits-util.h"
42 #include "list.h"
43 #include "log.h"
44 #include "mountpoint-util.h"
45 #include "nulstr-util.h"
46 #include "os-util.h"
47 #include "parse-util.h"
48 #include "path-util.h"
49 #include "percent-util.h"
50 #include "pidref.h"
51 #include "proc-cmdline.h"
52 #include "process-util.h"
53 #include "psi-util.h"
54 #include "selinux-util.h"
55 #include "smack-util.h"
56 #include "special.h"
57 #include "stat-util.h"
58 #include "string-table.h"
59 #include "string-util.h"
60 #include "strv.h"
61 #include "time-util.h"
62 #include "tomoyo-util.h"
63 #include "tpm2-util.h"
64 #include "uid-classification.h"
65 #include "user-util.h"
66 #include "virt.h"
67
68 Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate) {
69 Condition *c;
70
71 assert(type >= 0);
72 assert(type < _CONDITION_TYPE_MAX);
73 assert(parameter);
74
75 c = new(Condition, 1);
76 if (!c)
77 return NULL;
78
79 *c = (Condition) {
80 .type = type,
81 .trigger = trigger,
82 .negate = negate,
83 };
84
85 if (parameter) {
86 c->parameter = strdup(parameter);
87 if (!c->parameter)
88 return mfree(c);
89 }
90
91 return c;
92 }
93
94 Condition* condition_free(Condition *c) {
95 assert(c);
96
97 free(c->parameter);
98 return mfree(c);
99 }
100
101 Condition* condition_free_list_type(Condition *head, ConditionType type) {
102 LIST_FOREACH(conditions, c, head)
103 if (type < 0 || c->type == type) {
104 LIST_REMOVE(conditions, head, c);
105 condition_free(c);
106 }
107
108 assert(type >= 0 || !head);
109 return head;
110 }
111
112 static int condition_test_kernel_command_line(Condition *c, char **env) {
113 _cleanup_strv_free_ char **args = NULL;
114 int r;
115
116 assert(c);
117 assert(c->parameter);
118 assert(c->type == CONDITION_KERNEL_COMMAND_LINE);
119
120 r = proc_cmdline_strv(&args);
121 if (r < 0)
122 return r;
123
124 bool equal = strchr(c->parameter, '=');
125
126 STRV_FOREACH(word, args) {
127 bool found;
128
129 if (equal)
130 found = streq(*word, c->parameter);
131 else {
132 const char *f;
133
134 f = startswith(*word, c->parameter);
135 found = f && IN_SET(*f, 0, '=');
136 }
137
138 if (found)
139 return true;
140 }
141
142 return false;
143 }
144
145 static int condition_test_credential(Condition *c, char **env) {
146 int r;
147
148 assert(c);
149 assert(c->parameter);
150 assert(c->type == CONDITION_CREDENTIAL);
151
152 /* For now we'll do a very simple existence check and are happy with either a regular or an encrypted
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
159 int (*gd)(const char **ret);
160 FOREACH_ARGUMENT(gd, get_credentials_dir, get_encrypted_credentials_dir) {
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
174 r = access_nofollow(j, F_OK);
175 if (r >= 0)
176 return true; /* yay! */
177 if (r != -ENOENT)
178 return r;
179
180 /* not found in this dir */
181 }
182
183 return false;
184 }
185
186 static int condition_test_version_cmp(const char *condition, const char *ver) {
187 CompareOperator operator;
188 bool first = true;
189
190 assert(condition);
191 assert(ver);
192
193 for (const char *p = condition;;) {
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);
205 operator = parse_compare_operator(&s, COMPARE_ALLOW_FNMATCH|COMPARE_EQUAL_BY_STRING);
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)
219 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Unexpected end of expression: %s", p);
220 s = word;
221 } else
222 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Unexpected end of expression: %s", p);
223 }
224
225 r = version_or_fnmatch_compare(operator, ver, s);
226 if (r < 0)
227 return r;
228 if (!r)
229 return false;
230
231 first = false;
232 }
233
234 return true;
235 }
236
237 static 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
259 if (streq(word, "glibc"))
260 return condition_test_version_cmp(p, gnu_get_libc_version());
261
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
271 static int condition_test_osrelease(Condition *c, char **env) {
272 int r;
273
274 assert(c);
275 assert(c->type == CONDITION_OS_RELEASE);
276
277 for (const char *parameter = ASSERT_PTR(c->parameter);;) {
278 _cleanup_free_ char *key = NULL, *condition = NULL, *actual_value = NULL;
279 CompareOperator operator;
280 const char *word;
281
282 r = extract_first_word(&parameter, &condition, NULL, EXTRACT_UNQUOTE);
283 if (r < 0)
284 return log_debug_errno(r, "Failed to parse parameter: %m");
285 if (r == 0)
286 break;
287
288 /* parse_compare_operator() needs the string to start with the comparators */
289 word = condition;
290 r = extract_first_word(&word, &key, COMPARE_OPERATOR_WITH_FNMATCH_CHARS, EXTRACT_RETAIN_SEPARATORS);
291 if (r < 0)
292 return log_debug_errno(r, "Failed to parse parameter: %m");
293 /* The os-release spec mandates env-var-like key names */
294 if (r == 0 || isempty(word) || !env_name_is_valid(key))
295 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
296 "Failed to parse parameter, key/value format expected.");
297
298 /* Do not allow whitespace after the separator, as that's not a valid os-release format */
299 operator = parse_compare_operator(&word, COMPARE_ALLOW_FNMATCH|COMPARE_EQUAL_BY_STRING);
300 if (operator < 0 || isempty(word) || strchr(WHITESPACE, *word) != NULL)
301 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
302 "Failed to parse parameter, key/value format expected.");
303
304 r = parse_os_release(NULL, key, &actual_value);
305 if (r < 0)
306 return log_debug_errno(r, "Failed to parse os-release: %m");
307
308 /* 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);
311 if (r < 0)
312 return r;
313 if (!r)
314 return false;
315 }
316
317 return true;
318 }
319
320 static int condition_test_memory(Condition *c, char **env) {
321 CompareOperator operator;
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;
333 operator = parse_compare_operator(&p, 0);
334 if (operator < 0)
335 operator = COMPARE_GREATER_OR_EQUAL; /* default to >= check, if nothing is specified. */
336
337 r = parse_size(p, 1024, &k);
338 if (r < 0)
339 return log_debug_errno(r, "Failed to parse size '%s': %m", p);
340
341 return test_order(CMP(m, k), operator);
342 }
343
344 static int condition_test_cpus(Condition *c, char **env) {
345 CompareOperator operator;
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;
359 operator = parse_compare_operator(&p, 0);
360 if (operator < 0)
361 operator = COMPARE_GREATER_OR_EQUAL; /* default to >= check, if nothing is specified. */
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
367 return test_order(CMP((unsigned) n, k), operator);
368 }
369
370 static int condition_test_user(Condition *c, char **env) {
371 uid_t id;
372 int r;
373
374 assert(c);
375 assert(c->parameter);
376 assert(c->type == CONDITION_USER);
377
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
386 r = parse_uid(c->parameter, &id);
387 if (r >= 0)
388 return id == getuid() || id == geteuid();
389
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;
393
394 /* getusername_malloc() may do an nss lookup, which is not allowed in PID 1. */
395 _cleanup_free_ char *username = getusername_malloc();
396 if (!username)
397 return -ENOMEM;
398
399 if (streq(username, c->parameter))
400 return 1;
401
402 const char *u = c->parameter;
403 r = get_user_creds(&u, &id, NULL, NULL, NULL, USER_CREDS_ALLOW_MISSING);
404 if (r < 0)
405 return 0;
406
407 return id == getuid() || id == geteuid();
408 }
409
410 static int condition_test_control_group_controller(Condition *c, char **env) {
411 CGroupMask system_mask, wanted_mask;
412 int r;
413
414 assert(c);
415 assert(c->parameter);
416 assert(c->type == CONDITION_CONTROL_GROUP_CONTROLLER);
417
418 if (streq(c->parameter, "v2"))
419 return true;
420 if (streq(c->parameter, "v1"))
421 return false;
422
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);
433 return true;
434 }
435
436 return FLAGS_SET(system_mask, wanted_mask);
437 }
438
439 static int condition_test_group(Condition *c, char **env) {
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 */
452 if (getpid_cached() == 1)
453 return streq(c->parameter, "root");
454
455 return in_group(c->parameter) > 0;
456 }
457
458 static int condition_test_virtualization(Condition *c, char **env) {
459 Virtualization v;
460 int b;
461
462 assert(c);
463 assert(c->parameter);
464 assert(c->type == CONDITION_VIRTUALIZATION);
465
466 if (streq(c->parameter, "private-users"))
467 return running_in_userns();
468
469 v = detect_virtualization();
470 if (v < 0)
471 return v;
472
473 /* First, compare with yes/no */
474 b = parse_boolean(c->parameter);
475 if (b >= 0)
476 return b == (v != VIRTUALIZATION_NONE);
477
478 /* Then, compare categorization */
479 if (streq(c->parameter, "vm"))
480 return VIRTUALIZATION_IS_VM(v);
481
482 if (streq(c->parameter, "container"))
483 return VIRTUALIZATION_IS_CONTAINER(v);
484
485 /* Finally compare id */
486 return v != VIRTUALIZATION_NONE && streq(c->parameter, virtualization_to_string(v));
487 }
488
489 static int condition_test_architecture(Condition *c, char **env) {
490 Architecture a, b;
491
492 assert(c);
493 assert(c->parameter);
494 assert(c->type == CONDITION_ARCHITECTURE);
495
496 a = uname_architecture();
497 if (a < 0)
498 return a;
499
500 if (streq(c->parameter, "native"))
501 b = native_architecture();
502 else {
503 b = architecture_from_string(c->parameter);
504 if (b < 0) /* unknown architecture? Then it's definitely not ours */
505 return false;
506 }
507
508 return a == b;
509 }
510
511 #define DTCOMPAT_FILE "/proc/device-tree/compatible"
512 static 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)
522 log_debug_errno(r, "Failed to open '%s', assuming machine is incompatible: %m", DTCOMPAT_FILE);
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
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'. */
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
546 static int condition_test_firmware_smbios_field(const char *expression) {
547 _cleanup_free_ char *field = NULL, *expected_value = NULL, *actual_value = NULL;
548 CompareOperator operator;
549 int r;
550
551 assert(expression);
552
553 /* Parse SMBIOS field */
554 r = extract_first_word(&expression, &field, COMPARE_OPERATOR_WITH_FNMATCH_CHARS, EXTRACT_RETAIN_SEPARATORS);
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 */
564 operator = parse_compare_operator(&expression, COMPARE_ALLOW_FNMATCH|COMPARE_EQUAL_BY_STRING);
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))
577 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid SMBIOS field name.");
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 */
592 return version_or_fnmatch_compare(operator, actual_value, expected_value);
593 }
594
595 static int condition_test_firmware(Condition *c, char **env) {
596 sd_char *arg;
597 int r;
598
599 assert(c);
600 assert(c->parameter);
601 assert(c->type == CONDITION_FIRMWARE);
602
603 if (streq(c->parameter, "device-tree")) {
604 if (access("/sys/firmware/devicetree/", F_OK) < 0) {
605 if (errno != ENOENT)
606 log_debug_errno(errno, "Unexpected error when checking for /sys/firmware/devicetree/: %m");
607 return false;
608 } else
609 return true;
610 } else if ((arg = startswith(c->parameter, "device-tree-compatible("))) {
611 _cleanup_free_ char *dtc_arg = NULL;
612 char *end;
613
614 end = strrchr(arg, ')');
615 if (!end || *(end + 1) != '\0') {
616 log_debug("Malformed ConditionFirmware=%s", c->parameter);
617 return false;
618 }
619
620 dtc_arg = strndup(arg, end - arg);
621 if (!dtc_arg)
622 return -ENOMEM;
623
624 return condition_test_firmware_devicetree_compatible(dtc_arg);
625 } else if (streq(c->parameter, "uefi"))
626 return is_efi_boot();
627 else if ((arg = startswith(c->parameter, "smbios-field("))) {
628 _cleanup_free_ char *smbios_arg = NULL;
629 char *end;
630
631 end = strrchr(arg, ')');
632 if (!end || *(end + 1) != '\0')
633 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Malformed ConditionFirmware=%s.", c->parameter);
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 {
644 log_debug("Unsupported Firmware condition \"%s\"", c->parameter);
645 return false;
646 }
647 }
648
649 static int condition_test_host(Condition *c, char **env) {
650 _cleanup_free_ char *h = NULL;
651 int r;
652
653 assert(c);
654 assert(c->parameter);
655 assert(c->type == CONDITION_HOST);
656
657 sd_id128_t x;
658 if (sd_id128_from_string(c->parameter, &x) >= 0) {
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 }
678
679 /* Fall through, also allow setups where people set hostnames to UUIDs. Kinda weird, but no
680 * reason not to allow that */
681 }
682
683 h = gethostname_malloc();
684 if (!h)
685 return -ENOMEM;
686
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;
694 }
695
696 static int condition_test_ac_power(Condition *c, char **env) {
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)
705 return r;
706
707 return (on_ac_power() != 0) == !!r;
708 }
709
710 static int has_tpm2(void) {
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).
714 *
715 * Note that we don't check if we ourselves are built with TPM2 support here! */
716
717 return FLAGS_SET(tpm2_support_full(TPM2_SUPPORT_SUBSYSTEM|TPM2_SUPPORT_FIRMWARE), TPM2_SUPPORT_SUBSYSTEM|TPM2_SUPPORT_FIRMWARE);
718 }
719
720 static int condition_test_security(Condition *c, char **env) {
721 assert(c);
722 assert(c->parameter);
723 assert(c->type == CONDITION_SECURITY);
724
725 if (streq(c->parameter, "selinux"))
726 return mac_selinux_use();
727 if (streq(c->parameter, "smack"))
728 return mac_smack_use();
729 if (streq(c->parameter, "apparmor"))
730 return mac_apparmor_use();
731 if (streq(c->parameter, "audit"))
732 return use_audit();
733 if (streq(c->parameter, "ima"))
734 return use_ima();
735 if (streq(c->parameter, "tomoyo"))
736 return mac_tomoyo_use();
737 if (streq(c->parameter, "uefi-secureboot"))
738 return is_efi_secure_boot();
739 if (streq(c->parameter, "tpm2"))
740 return has_tpm2();
741 if (streq(c->parameter, "cvm"))
742 return detect_confidential_virtualization() > 0;
743 if (streq(c->parameter, "measured-uki"))
744 return efi_measured_uki(LOG_DEBUG);
745
746 return false;
747 }
748
749 static int condition_test_capability(Condition *c, char **env) {
750 int r;
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 */
757 int value = capability_from_name(c->parameter);
758 if (value < 0)
759 return -EINVAL;
760
761 CapabilityQuintet q;
762 r = pidref_get_capability(&PIDREF_MAKE_FROM_PID(getpid_cached()), &q);
763 if (r < 0)
764 return r;
765
766 return BIT_SET(q.bounding, value);
767 }
768
769 static int condition_test_needs_update(Condition *c, char **env) {
770 struct stat usr, other;
771 const char *p;
772 bool b;
773 int r;
774
775 assert(c);
776 assert(c->parameter);
777 assert(c->type == CONDITION_NEEDS_UPDATE);
778
779 r = proc_cmdline_get_bool("systemd.condition_needs_update", /* flags = */ 0, &b);
780 if (r < 0)
781 log_debug_errno(r, "Failed to parse systemd.condition_needs_update= kernel command line argument, ignoring: %m");
782 if (r > 0)
783 return b;
784
785 if (in_initrd()) {
786 log_debug("We are in an initrd, not doing any updates.");
787 return false;
788 }
789
790 if (!path_is_absolute(c->parameter)) {
791 log_debug("Specified condition parameter '%s' is not absolute, assuming an update is needed.", c->parameter);
792 return true;
793 }
794
795 /* If the file system is read-only we shouldn't suggest an update */
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)
800 return false;
801
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. */
804
805 p = strjoina(c->parameter, "/.updated");
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);
809 return true;
810 }
811
812 if (lstat("/usr/", &usr) < 0) {
813 log_debug_errno(errno, "Failed to stat() /usr/, assuming an update is needed: %m");
814 return true;
815 }
816
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 */
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;
833
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;
839 }
840 if (isempty(timestamp_str)) {
841 log_debug("No data in timestamp file '%s', using mtime.", p);
842 return true;
843 }
844
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;
850 }
851
852 return timespec_load_nsec(&usr.st_mtim) > timestamp;
853 }
854
855 static 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
877 static int condition_test_first_boot(Condition *c, char **env) {
878 int r;
879
880 assert(c);
881 assert(c->parameter);
882 assert(c->type == CONDITION_FIRST_BOOT);
883
884 // TODO: Parse c->parameter immediately when reading the config.
885 // Apply negation when parsing too.
886
887 r = parse_boolean(c->parameter);
888 if (r < 0)
889 return r;
890
891 return in_first_boot() == r;
892 }
893
894 static int condition_test_environment(Condition *c, char **env) {
895 bool equal;
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
922 static int condition_test_path_exists(Condition *c, char **env) {
923 assert(c);
924 assert(c->parameter);
925 assert(c->type == CONDITION_PATH_EXISTS);
926
927 return access(c->parameter, F_OK) >= 0;
928 }
929
930 static int condition_test_path_exists_glob(Condition *c, char **env) {
931 assert(c);
932 assert(c->parameter);
933 assert(c->type == CONDITION_PATH_EXISTS_GLOB);
934
935 return glob_exists(c->parameter) > 0;
936 }
937
938 static int condition_test_path_is_directory(Condition *c, char **env) {
939 assert(c);
940 assert(c->parameter);
941 assert(c->type == CONDITION_PATH_IS_DIRECTORY);
942
943 return is_dir(c->parameter, true) > 0;
944 }
945
946 static int condition_test_path_is_symbolic_link(Condition *c, char **env) {
947 assert(c);
948 assert(c->parameter);
949 assert(c->type == CONDITION_PATH_IS_SYMBOLIC_LINK);
950
951 return is_symlink(c->parameter) > 0;
952 }
953
954 static int condition_test_path_is_mount_point(Condition *c, char **env) {
955 assert(c);
956 assert(c->parameter);
957 assert(c->type == CONDITION_PATH_IS_MOUNT_POINT);
958
959 return path_is_mount_point_full(c->parameter, /* root = */ NULL, AT_SYMLINK_FOLLOW) > 0;
960 }
961
962 static int condition_test_path_is_read_write(Condition *c, char **env) {
963 int r;
964
965 assert(c);
966 assert(c->parameter);
967 assert(c->type == CONDITION_PATH_IS_READ_WRITE);
968
969 r = path_is_read_only_fs(c->parameter);
970
971 return r <= 0 && r != -ENOENT;
972 }
973
974 static 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
982 static int condition_test_path_is_encrypted(Condition *c, char **env) {
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
996 static int condition_test_directory_not_empty(Condition *c, char **env) {
997 int r;
998
999 assert(c);
1000 assert(c->parameter);
1001 assert(c->type == CONDITION_DIRECTORY_NOT_EMPTY);
1002
1003 r = dir_is_empty(c->parameter, /* ignore_hidden_or_backup= */ true);
1004 return r <= 0 && !IN_SET(r, -ENOENT, -ENOTDIR);
1005 }
1006
1007 static int condition_test_file_not_empty(Condition *c, char **env) {
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) &&
1016 st.st_size > 0);
1017 }
1018
1019 static int condition_test_file_is_executable(Condition *c, char **env) {
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) &&
1028 (st.st_mode & 0111));
1029 }
1030
1031 static 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;
1036 PressureType preferred_pressure_type = PRESSURE_TYPE_FULL;
1037 int r;
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;
1053 r = extract_many_words(&p, ":", 0, &first, &second);
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) {
1058 /* cpu.pressure 'full' is reported but undefined at system level */
1059 if (c->type == CONDITION_CPU_PRESSURE)
1060 preferred_pressure_type = PRESSURE_TYPE_SOME;
1061
1062 pressure_path = path_join("/proc/pressure", pressure_type);
1063 if (!pressure_path)
1064 return log_oom_debug();
1065
1066 value = first;
1067 } else {
1068 const char *controller = strjoina(pressure_type, ".pressure");
1069 _cleanup_free_ char *slice_path = NULL, *root_scope = NULL;
1070 CGroupMask mask, required_mask;
1071 char *slice, *e;
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)
1079 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to parse condition parameter %s.", c->parameter);
1080
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
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
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. */
1124 r = extract_many_words(&value, "/", 0, &third, &fourth);
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)
1134 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to parse condition parameter %s.", c->parameter);
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
1143 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to parse condition parameter %s.", c->parameter);
1144 }
1145
1146 value = strstrip(third);
1147 if (!value)
1148 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to parse condition parameter %s.", c->parameter);
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
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)
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
1174 static 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
1227 int condition_test(Condition *c, char **env) {
1228
1229 static int (*const condition_tests[_CONDITION_TYPE_MAX])(Condition *c, char **env) = {
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,
1236 [CONDITION_PATH_IS_ENCRYPTED] = condition_test_path_is_encrypted,
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,
1241 [CONDITION_VERSION] = condition_test_version,
1242 [CONDITION_CREDENTIAL] = condition_test_credential,
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,
1249 [CONDITION_FIRMWARE] = condition_test_firmware,
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,
1254 [CONDITION_CONTROL_GROUP_CONTROLLER] = condition_test_control_group_controller,
1255 [CONDITION_CPUS] = condition_test_cpus,
1256 [CONDITION_MEMORY] = condition_test_memory,
1257 [CONDITION_ENVIRONMENT] = condition_test_environment,
1258 [CONDITION_CPU_FEATURE] = condition_test_cpufeature,
1259 [CONDITION_OS_RELEASE] = condition_test_osrelease,
1260 [CONDITION_MEMORY_PRESSURE] = condition_test_psi,
1261 [CONDITION_CPU_PRESSURE] = condition_test_psi,
1262 [CONDITION_IO_PRESSURE] = condition_test_psi,
1263 [CONDITION_KERNEL_MODULE_LOADED] = condition_test_kernel_module_loaded,
1264 };
1265
1266 int r, b;
1267
1268 assert(c);
1269 assert(c->type >= 0);
1270 assert(c->type < _CONDITION_TYPE_MAX);
1271
1272 r = condition_tests[c->type](c, env);
1273 if (r < 0) {
1274 c->result = CONDITION_ERROR;
1275 return r;
1276 }
1277
1278 b = (r > 0) == !c->negate;
1279 c->result = b ? CONDITION_SUCCEEDED : CONDITION_FAILED;
1280 return b;
1281 }
1282
1283 bool condition_test_list(
1284 Condition *first,
1285 char **env,
1286 condition_to_string_t to_string,
1287 condition_test_logger_t logger,
1288 void *userdata) {
1289
1290 int triggered = -1;
1291
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
1302 r = condition_test(c, env);
1303
1304 if (logger) {
1305 if (r < 0)
1306 logger(userdata, LOG_WARNING, r, PROJECT_FILE, __LINE__, __func__,
1307 "Couldn't determine result for %s=%s%s%s, assuming failed: %m",
1308 to_string(c->type),
1309 c->trigger ? "|" : "",
1310 c->negate ? "!" : "",
1311 c->parameter);
1312 else
1313 logger(userdata, LOG_DEBUG, 0, PROJECT_FILE, __LINE__, __func__,
1314 "%s=%s%s%s %s.",
1315 to_string(c->type),
1316 c->trigger ? "|" : "",
1317 c->negate ? "!" : "",
1318 c->parameter,
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
1332 void condition_dump(Condition *c, FILE *f, const char *prefix, condition_to_string_t to_string) {
1333 assert(c);
1334 assert(f);
1335 assert(to_string);
1336
1337 prefix = strempty(prefix);
1338
1339 fprintf(f,
1340 "%s\t%s: %s%s%s %s\n",
1341 prefix,
1342 to_string(c->type),
1343 c->trigger ? "|" : "",
1344 c->negate ? "!" : "",
1345 c->parameter,
1346 condition_result_to_string(c->result));
1347 }
1348
1349 void condition_dump_list(Condition *first, FILE *f, const char *prefix, condition_to_string_t to_string) {
1350 LIST_FOREACH(conditions, c, first)
1351 condition_dump(c, f, prefix, to_string);
1352 }
1353
1354 static const char* const _condition_type_table[_CONDITION_TYPE_MAX] = {
1355 [CONDITION_ARCHITECTURE] = "ConditionArchitecture",
1356 [CONDITION_FIRMWARE] = "ConditionFirmware",
1357 [CONDITION_VIRTUALIZATION] = "ConditionVirtualization",
1358 [CONDITION_HOST] = "ConditionHost",
1359 [CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine",
1360 [CONDITION_VERSION] = "ConditionVersion",
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",
1379 [CONDITION_CONTROL_GROUP_CONTROLLER] = "ConditionControlGroupController",
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",
1389 };
1390
1391 DEFINE_PRIVATE_STRING_TABLE_LOOKUP(_condition_type, ConditionType);
1392
1393 const char* condition_type_to_string(ConditionType t) {
1394 return _condition_type_to_string(t);
1395 }
1396
1397 ConditionType condition_type_from_string(const char *s) {
1398 /* for backward compatibility */
1399 if (streq_ptr(s, "ConditionKernelVersion"))
1400 return CONDITION_VERSION;
1401
1402 return _condition_type_from_string(s);
1403 }
1404
1405 void condition_types_list(void) {
1406 DUMP_STRING_TABLE(_condition_type, ConditionType, _CONDITION_TYPE_MAX);
1407 }
1408
1409 static const char* const _assert_type_table[_CONDITION_TYPE_MAX] = {
1410 [CONDITION_ARCHITECTURE] = "AssertArchitecture",
1411 [CONDITION_FIRMWARE] = "AssertFirmware",
1412 [CONDITION_VIRTUALIZATION] = "AssertVirtualization",
1413 [CONDITION_HOST] = "AssertHost",
1414 [CONDITION_KERNEL_COMMAND_LINE] = "AssertKernelCommandLine",
1415 [CONDITION_VERSION] = "AssertVersion",
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",
1434 [CONDITION_CONTROL_GROUP_CONTROLLER] = "AssertControlGroupController",
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",
1444 };
1445
1446 DEFINE_PRIVATE_STRING_TABLE_LOOKUP(_assert_type, ConditionType);
1447
1448 const char* assert_type_to_string(ConditionType t) {
1449 return _assert_type_to_string(t);
1450 }
1451
1452 ConditionType 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 }
1459
1460 void assert_types_list(void) {
1461 DUMP_STRING_TABLE(_assert_type, ConditionType, _CONDITION_TYPE_MAX);
1462 }
1463
1464 static const char* const condition_result_table[_CONDITION_RESULT_MAX] = {
1465 [CONDITION_UNTESTED] = "untested",
1466 [CONDITION_SUCCEEDED] = "succeeded",
1467 [CONDITION_FAILED] = "failed",
1468 [CONDITION_ERROR] = "error",
1469 };
1470
1471 DEFINE_STRING_TABLE_LOOKUP(condition_result, ConditionResult);