1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
5 #include <gnu/libc-version.h>
7 #include <sys/utsname.h>
13 #include "alloc-util.h"
14 #include "apparmor-util.h"
15 #include "architecture.h"
16 #include "battery-util.h"
18 #include "blockdev-util.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"
31 #include "extract-word.h"
35 #include "glob-util.h"
36 #include "hostname-setup.h"
37 #include "id128-util.h"
39 #include "initrd-util.h"
40 #include "libaudit-util.h"
41 #include "limits-util.h"
44 #include "mountpoint-util.h"
45 #include "nulstr-util.h"
47 #include "parse-util.h"
48 #include "path-util.h"
49 #include "percent-util.h"
51 #include "proc-cmdline.h"
52 #include "process-util.h"
54 #include "selinux-util.h"
55 #include "smack-util.h"
57 #include "stat-util.h"
58 #include "string-table.h"
59 #include "string-util.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"
68 Condition
* condition_new(ConditionType type
, const char *parameter
, bool trigger
, bool negate
) {
72 assert(type
< _CONDITION_TYPE_MAX
);
75 c
= new(Condition
, 1);
86 c
->parameter
= strdup(parameter
);
94 Condition
* condition_free(Condition
*c
) {
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
);
108 assert(type
>= 0 || !head
);
112 static int condition_test_kernel_command_line(Condition
*c
, char **env
) {
113 _cleanup_strv_free_
char **args
= NULL
;
117 assert(c
->parameter
);
118 assert(c
->type
== CONDITION_KERNEL_COMMAND_LINE
);
120 r
= proc_cmdline_strv(&args
);
124 bool equal
= strchr(c
->parameter
, '=');
126 STRV_FOREACH(word
, args
) {
130 found
= streq(*word
, c
->parameter
);
134 f
= startswith(*word
, c
->parameter
);
135 found
= f
&& IN_SET(*f
, 0, '=');
145 static int condition_test_credential(Condition
*c
, char **env
) {
149 assert(c
->parameter
);
150 assert(c
->type
== CONDITION_CREDENTIAL
);
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. */
156 if (!credential_name_valid(c
->parameter
)) /* credentials with invalid names do not exist */
159 int (*gd
)(const char **ret
);
160 FOREACH_ARGUMENT(gd
, get_credentials_dir
, get_encrypted_credentials_dir
) {
161 _cleanup_free_
char *j
= NULL
;
165 if (r
== -ENXIO
) /* no env var set */
170 j
= path_join(cd
, c
->parameter
);
174 r
= access_nofollow(j
, F_OK
);
176 return true; /* yay! */
180 /* not found in this dir */
186 static int condition_test_version_cmp(const char *condition
, const char *ver
) {
187 CompareOperator
operator;
193 for (const char *p
= condition
;;) {
194 _cleanup_free_
char *word
= NULL
;
198 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_UNQUOTE
);
200 return log_debug_errno(r
, "Failed to parse condition string \"%s\": %m", p
);
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
;
209 s
+= strspn(s
, WHITESPACE
);
212 /* For backwards compatibility, allow whitespace between the operator and
213 * value, without quoting, but only in the first expression. */
215 r
= extract_first_word(&p
, &word
, NULL
, 0);
217 return log_debug_errno(r
, "Failed to parse condition string \"%s\": %m", p
);
219 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL
), "Unexpected end of expression: %s", p
);
222 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL
), "Unexpected end of expression: %s", p
);
225 r
= version_or_fnmatch_compare(operator, ver
, s
);
237 static int condition_test_version(Condition
*c
, char **env
) {
241 assert(c
->type
== CONDITION_VERSION
);
243 /* An empty condition is considered true. */
244 if (isempty(c
->parameter
))
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
);
252 return log_debug_errno(r
, "Failed to parse compare predicate \"%s\": %m", p
);
254 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL
), "Missing right operand in condition: %s", c
->parameter
);
256 if (streq(word
, "systemd"))
257 return condition_test_version_cmp(p
, STRINGIFY(PROJECT_VERSION
));
259 if (streq(word
, "glibc"))
260 return condition_test_version_cmp(p
, gnu_get_libc_version());
262 /* if no predicate has been set, default to "kernel" and use the whole parameter as condition */
263 if (!streq(word
, "kernel"))
267 assert_se(uname(&u
) >= 0);
268 return condition_test_version_cmp(p
, u
.release
);
271 static int condition_test_osrelease(Condition
*c
, char **env
) {
275 assert(c
->type
== CONDITION_OS_RELEASE
);
277 for (const char *parameter
= ASSERT_PTR(c
->parameter
);;) {
278 _cleanup_free_
char *key
= NULL
, *condition
= NULL
, *actual_value
= NULL
;
279 CompareOperator
operator;
282 r
= extract_first_word(¶meter
, &condition
, NULL
, EXTRACT_UNQUOTE
);
284 return log_debug_errno(r
, "Failed to parse parameter: %m");
288 /* parse_compare_operator() needs the string to start with the comparators */
290 r
= extract_first_word(&word
, &key
, COMPARE_OPERATOR_WITH_FNMATCH_CHARS
, EXTRACT_RETAIN_SEPARATORS
);
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.");
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.");
304 r
= parse_os_release(NULL
, key
, &actual_value
);
306 return log_debug_errno(r
, "Failed to parse os-release: %m");
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
);
320 static int condition_test_memory(Condition
*c
, char **env
) {
321 CompareOperator
operator;
327 assert(c
->parameter
);
328 assert(c
->type
== CONDITION_MEMORY
);
330 m
= physical_memory();
333 operator = parse_compare_operator(&p
, 0);
335 operator = COMPARE_GREATER_OR_EQUAL
; /* default to >= check, if nothing is specified. */
337 r
= parse_size(p
, 1024, &k
);
339 return log_debug_errno(r
, "Failed to parse size '%s': %m", p
);
341 return test_order(CMP(m
, k
), operator);
344 static int condition_test_cpus(Condition
*c
, char **env
) {
345 CompareOperator
operator;
351 assert(c
->parameter
);
352 assert(c
->type
== CONDITION_CPUS
);
354 n
= cpus_in_affinity_mask();
356 return log_debug_errno(n
, "Failed to determine CPUs in affinity mask: %m");
359 operator = parse_compare_operator(&p
, 0);
361 operator = COMPARE_GREATER_OR_EQUAL
; /* default to >= check, if nothing is specified. */
363 r
= safe_atou(p
, &k
);
365 return log_debug_errno(r
, "Failed to parse number of CPUs: %m");
367 return test_order(CMP((unsigned) n
, k
), operator);
370 static int condition_test_user(Condition
*c
, char **env
) {
375 assert(c
->parameter
);
376 assert(c
->type
== CONDITION_USER
);
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());
386 r
= parse_uid(c
->parameter
, &id
);
388 return id
== getuid() || id
== geteuid();
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. */
394 /* getusername_malloc() may do an nss lookup, which is not allowed in PID 1. */
395 _cleanup_free_
char *username
= getusername_malloc();
399 if (streq(username
, c
->parameter
))
402 const char *u
= c
->parameter
;
403 r
= get_user_creds(&u
, &id
, NULL
, NULL
, NULL
, USER_CREDS_ALLOW_MISSING
);
407 return id
== getuid() || id
== geteuid();
410 static int condition_test_control_group_controller(Condition
*c
, char **env
) {
411 CGroupMask system_mask
, wanted_mask
;
415 assert(c
->parameter
);
416 assert(c
->type
== CONDITION_CONTROL_GROUP_CONTROLLER
);
418 if (streq(c
->parameter
, "v2"))
420 if (streq(c
->parameter
, "v1"))
423 r
= cg_mask_supported(&system_mask
);
425 return log_debug_errno(r
, "Failed to determine supported controllers: %m");
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
);
436 return FLAGS_SET(system_mask
, wanted_mask
);
439 static int condition_test_group(Condition
*c
, char **env
) {
444 assert(c
->parameter
);
445 assert(c
->type
== CONDITION_GROUP
);
447 r
= parse_gid(c
->parameter
, &id
);
451 /* Avoid any NSS lookups if we are PID1 */
452 if (getpid_cached() == 1)
453 return streq(c
->parameter
, "root");
455 return in_group(c
->parameter
) > 0;
458 static int condition_test_virtualization(Condition
*c
, char **env
) {
463 assert(c
->parameter
);
464 assert(c
->type
== CONDITION_VIRTUALIZATION
);
466 if (streq(c
->parameter
, "private-users"))
467 return running_in_userns();
469 v
= detect_virtualization();
473 /* First, compare with yes/no */
474 b
= parse_boolean(c
->parameter
);
476 return b
== (v
!= VIRTUALIZATION_NONE
);
478 /* Then, compare categorization */
479 if (streq(c
->parameter
, "vm"))
480 return VIRTUALIZATION_IS_VM(v
);
482 if (streq(c
->parameter
, "container"))
483 return VIRTUALIZATION_IS_CONTAINER(v
);
485 /* Finally compare id */
486 return v
!= VIRTUALIZATION_NONE
&& streq(c
->parameter
, virtualization_to_string(v
));
489 static int condition_test_architecture(Condition
*c
, char **env
) {
493 assert(c
->parameter
);
494 assert(c
->type
== CONDITION_ARCHITECTURE
);
496 a
= uname_architecture();
500 if (streq(c
->parameter
, "native"))
501 b
= native_architecture();
503 b
= architecture_from_string(c
->parameter
);
504 if (b
< 0) /* unknown architecture? Then it's definitely not ours */
511 #define DTCOMPAT_FILE "/proc/device-tree/compatible"
512 static int condition_test_firmware_devicetree_compatible(const char *dtcarg
) {
514 _cleanup_free_
char *dtcompat
= NULL
;
515 _cleanup_strv_free_
char **dtcompatlist
= NULL
;
518 r
= read_full_virtual_file(DTCOMPAT_FILE
, &dtcompat
, &size
);
520 /* if the path doesn't exist it is incompatible */
522 log_debug_errno(r
, "Failed to open '%s', assuming machine is incompatible: %m", DTCOMPAT_FILE
);
526 /* Not sure this can happen, but play safe. */
528 log_debug("%s has zero length, assuming machine is incompatible", DTCOMPAT_FILE
);
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
);
539 dtcompatlist
= strv_parse_nulstr(dtcompat
, size
);
543 return strv_contains(dtcompatlist
, dtcarg
);
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;
553 /* Parse SMBIOS field */
554 r
= extract_first_word(&expression
, &field
, COMPARE_OPERATOR_WITH_FNMATCH_CHARS
, EXTRACT_RETAIN_SEPARATORS
);
557 if (r
== 0 || isempty(expression
))
560 /* Remove trailing spaces from SMBIOS field */
561 delete_trailing_chars(field
, WHITESPACE
);
564 operator = parse_compare_operator(&expression
, COMPARE_ALLOW_FNMATCH
|COMPARE_EQUAL_BY_STRING
);
568 /* Parse expected value */
569 r
= extract_first_word(&expression
, &expected_value
, NULL
, EXTRACT_UNQUOTE
);
572 if (r
== 0 || !isempty(expression
))
575 /* Read actual value from sysfs */
576 if (!filename_is_valid(field
))
577 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL
), "Invalid SMBIOS field name.");
579 const char *p
= strjoina("/sys/class/dmi/id/", field
);
580 r
= read_virtual_file(p
, SIZE_MAX
, &actual_value
, NULL
);
582 log_debug_errno(r
, "Failed to read %s: %m", p
);
588 /* Remove trailing newline */
589 delete_trailing_chars(actual_value
, WHITESPACE
);
591 /* Finally compare actual and expected value */
592 return version_or_fnmatch_compare(operator, actual_value
, expected_value
);
595 static int condition_test_firmware(Condition
*c
, char **env
) {
600 assert(c
->parameter
);
601 assert(c
->type
== CONDITION_FIRMWARE
);
603 if (streq(c
->parameter
, "device-tree")) {
604 if (access("/sys/firmware/devicetree/", F_OK
) < 0) {
606 log_debug_errno(errno
, "Unexpected error when checking for /sys/firmware/devicetree/: %m");
610 } else if ((arg
= startswith(c
->parameter
, "device-tree-compatible("))) {
611 _cleanup_free_
char *dtc_arg
= NULL
;
614 end
= strrchr(arg
, ')');
615 if (!end
|| *(end
+ 1) != '\0') {
616 log_debug("Malformed ConditionFirmware=%s", c
->parameter
);
620 dtc_arg
= strndup(arg
, end
- arg
);
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
;
631 end
= strrchr(arg
, ')');
632 if (!end
|| *(end
+ 1) != '\0')
633 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL
), "Malformed ConditionFirmware=%s.", c
->parameter
);
635 smbios_arg
= strndup(arg
, end
- arg
);
637 return log_oom_debug();
639 r
= condition_test_firmware_smbios_field(smbios_arg
);
641 return log_debug_errno(r
, "Malformed ConditionFirmware=%s: %m", c
->parameter
);
644 log_debug("Unsupported Firmware condition \"%s\"", c
->parameter
);
649 static int condition_test_host(Condition
*c
, char **env
) {
650 _cleanup_free_
char *h
= NULL
;
654 assert(c
->parameter
);
655 assert(c
->type
== CONDITION_HOST
);
658 if (sd_id128_from_string(c
->parameter
, &x
) >= 0) {
659 static const struct {
661 int (*get_id
)(sd_id128_t
*ret
);
663 { "machine ID", sd_id128_get_machine
},
664 { "boot ID", sd_id128_get_boot
},
665 { "product UUID", id128_get_product
},
668 /* If this is a UUID, check if this matches the machine ID, boot ID or product UUID */
669 FOREACH_ELEMENT(i
, table
) {
674 log_debug_errno(r
, "Failed to get %s, ignoring: %m", i
->name
);
675 else if (sd_id128_equal(x
, y
))
679 /* Fall through, also allow setups where people set hostnames to UUIDs. Kinda weird, but no
680 * reason not to allow that */
683 h
= gethostname_malloc();
687 r
= fnmatch(c
->parameter
, h
, FNM_CASEFOLD
);
688 if (r
== FNM_NOMATCH
)
691 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL
), "fnmatch() failed.");
696 static int condition_test_ac_power(Condition
*c
, char **env
) {
700 assert(c
->parameter
);
701 assert(c
->type
== CONDITION_AC_POWER
);
703 r
= parse_boolean(c
->parameter
);
707 return (on_ac_power() != 0) == !!r
;
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).
715 * Note that we don't check if we ourselves are built with TPM2 support here! */
717 return FLAGS_SET(tpm2_support_full(TPM2_SUPPORT_SUBSYSTEM
|TPM2_SUPPORT_FIRMWARE
), TPM2_SUPPORT_SUBSYSTEM
|TPM2_SUPPORT_FIRMWARE
);
720 static int condition_test_security(Condition
*c
, char **env
) {
722 assert(c
->parameter
);
723 assert(c
->type
== CONDITION_SECURITY
);
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"))
733 if (streq(c
->parameter
, "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"))
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
);
749 static int condition_test_capability(Condition
*c
, char **env
) {
753 assert(c
->parameter
);
754 assert(c
->type
== CONDITION_CAPABILITY
);
756 /* If it's an invalid capability, we don't have it */
757 int value
= capability_from_name(c
->parameter
);
762 r
= pidref_get_capability(&PIDREF_MAKE_FROM_PID(getpid_cached()), &q
);
766 return BIT_SET(q
.bounding
, value
);
769 static int condition_test_needs_update(Condition
*c
, char **env
) {
770 struct stat usr
, other
;
776 assert(c
->parameter
);
777 assert(c
->type
== CONDITION_NEEDS_UPDATE
);
779 r
= proc_cmdline_get_bool("systemd.condition_needs_update", /* flags = */ 0, &b
);
781 log_debug_errno(r
, "Failed to parse systemd.condition_needs_update= kernel command line argument, ignoring: %m");
786 log_debug("We are in an initrd, not doing any updates.");
790 if (!path_is_absolute(c
->parameter
)) {
791 log_debug("Specified condition parameter '%s' is not absolute, assuming an update is needed.", c
->parameter
);
795 /* If the file system is read-only we shouldn't suggest an update */
796 r
= path_is_read_only_fs(c
->parameter
);
798 log_debug_errno(r
, "Failed to determine if '%s' is read-only, ignoring: %m", c
->parameter
);
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. */
805 p
= strjoina(c
->parameter
, "/.updated");
806 if (lstat(p
, &other
) < 0) {
808 log_debug_errno(errno
, "Failed to stat() '%s', assuming an update is needed: %m", p
);
812 if (lstat("/usr/", &usr
) < 0) {
813 log_debug_errno(errno
, "Failed to stat() /usr/, assuming an update is needed: %m");
818 * First, compare seconds as they are always accurate...
820 if (usr
.st_mtim
.tv_sec
!= other
.st_mtim
.tv_sec
)
821 return usr
.st_mtim
.tv_sec
> other
.st_mtim
.tv_sec
;
824 * ...then compare nanoseconds.
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)).
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
;
834 _cleanup_free_
char *timestamp_str
= NULL
;
835 r
= parse_env_file(NULL
, p
, "TIMESTAMP_NSEC", ×tamp_str
);
837 log_debug_errno(r
, "Failed to parse timestamp file '%s', using mtime: %m", p
);
840 if (isempty(timestamp_str
)) {
841 log_debug("No data in timestamp file '%s', using mtime.", p
);
846 r
= safe_atou64(timestamp_str
, ×tamp
);
848 log_debug_errno(r
, "Failed to parse timestamp value '%s' in file '%s', using mtime: %m", timestamp_str
, p
);
852 return timespec_load_nsec(&usr
.st_mtim
) > timestamp
;
855 static bool in_first_boot(void) {
856 static int first_boot
= -1;
862 const char *e
= secure_getenv("SYSTEMD_FIRST_BOOT");
864 r
= parse_boolean(e
);
866 log_debug_errno(r
, "Failed to parse $SYSTEMD_FIRST_BOOT, ignoring: %m");
868 return (first_boot
= r
);
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");
877 static int condition_test_first_boot(Condition
*c
, char **env
) {
881 assert(c
->parameter
);
882 assert(c
->type
== CONDITION_FIRST_BOOT
);
884 // TODO: Parse c->parameter immediately when reading the config.
885 // Apply negation when parsing too.
887 r
= parse_boolean(c
->parameter
);
891 return in_first_boot() == r
;
894 static int condition_test_environment(Condition
*c
, char **env
) {
898 assert(c
->parameter
);
899 assert(c
->type
== CONDITION_ENVIRONMENT
);
901 equal
= strchr(c
->parameter
, '=');
903 STRV_FOREACH(i
, env
) {
907 found
= streq(c
->parameter
, *i
);
911 f
= startswith(*i
, c
->parameter
);
912 found
= f
&& IN_SET(*f
, 0, '=');
922 static int condition_test_path_exists(Condition
*c
, char **env
) {
924 assert(c
->parameter
);
925 assert(c
->type
== CONDITION_PATH_EXISTS
);
927 return access(c
->parameter
, F_OK
) >= 0;
930 static int condition_test_path_exists_glob(Condition
*c
, char **env
) {
932 assert(c
->parameter
);
933 assert(c
->type
== CONDITION_PATH_EXISTS_GLOB
);
935 return glob_exists(c
->parameter
) > 0;
938 static int condition_test_path_is_directory(Condition
*c
, char **env
) {
940 assert(c
->parameter
);
941 assert(c
->type
== CONDITION_PATH_IS_DIRECTORY
);
943 return is_dir(c
->parameter
, true) > 0;
946 static int condition_test_path_is_symbolic_link(Condition
*c
, char **env
) {
948 assert(c
->parameter
);
949 assert(c
->type
== CONDITION_PATH_IS_SYMBOLIC_LINK
);
951 return is_symlink(c
->parameter
) > 0;
954 static int condition_test_path_is_mount_point(Condition
*c
, char **env
) {
956 assert(c
->parameter
);
957 assert(c
->type
== CONDITION_PATH_IS_MOUNT_POINT
);
959 return path_is_mount_point_full(c
->parameter
, /* root = */ NULL
, AT_SYMLINK_FOLLOW
) > 0;
962 static int condition_test_path_is_read_write(Condition
*c
, char **env
) {
966 assert(c
->parameter
);
967 assert(c
->type
== CONDITION_PATH_IS_READ_WRITE
);
969 r
= path_is_read_only_fs(c
->parameter
);
971 return r
<= 0 && r
!= -ENOENT
;
974 static int condition_test_cpufeature(Condition
*c
, char **env
) {
976 assert(c
->parameter
);
977 assert(c
->type
== CONDITION_CPU_FEATURE
);
979 return has_cpu_with_flag(ascii_strlower(c
->parameter
));
982 static int condition_test_path_is_encrypted(Condition
*c
, char **env
) {
986 assert(c
->parameter
);
987 assert(c
->type
== CONDITION_PATH_IS_ENCRYPTED
);
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
);
996 static int condition_test_directory_not_empty(Condition
*c
, char **env
) {
1000 assert(c
->parameter
);
1001 assert(c
->type
== CONDITION_DIRECTORY_NOT_EMPTY
);
1003 r
= dir_is_empty(c
->parameter
, /* ignore_hidden_or_backup= */ true);
1004 return r
<= 0 && !IN_SET(r
, -ENOENT
, -ENOTDIR
);
1007 static int condition_test_file_not_empty(Condition
*c
, char **env
) {
1011 assert(c
->parameter
);
1012 assert(c
->type
== CONDITION_FILE_NOT_EMPTY
);
1014 return (stat(c
->parameter
, &st
) >= 0 &&
1015 S_ISREG(st
.st_mode
) &&
1019 static int condition_test_file_is_executable(Condition
*c
, char **env
) {
1023 assert(c
->parameter
);
1024 assert(c
->type
== CONDITION_FILE_IS_EXECUTABLE
);
1026 return (stat(c
->parameter
, &st
) >= 0 &&
1027 S_ISREG(st
.st_mode
) &&
1028 (st
.st_mode
& 0111));
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
;
1040 assert(c
->parameter
);
1041 assert(IN_SET(c
->type
, CONDITION_MEMORY_PRESSURE
, CONDITION_CPU_PRESSURE
, CONDITION_IO_PRESSURE
));
1043 if (!is_pressure_supported()) {
1044 log_debug("Pressure Stall Information (PSI) is not supported, skipping.");
1048 pressure_type
= c
->type
== CONDITION_MEMORY_PRESSURE
? "memory" :
1049 c
->type
== CONDITION_CPU_PRESSURE
? "cpu" :
1053 r
= extract_many_words(&p
, ":", 0, &first
, &second
);
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. */
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
;
1062 pressure_path
= path_join("/proc/pressure", pressure_type
);
1064 return log_oom_debug();
1068 const char *controller
= strjoina(pressure_type
, ".pressure");
1069 _cleanup_free_
char *slice_path
= NULL
, *root_scope
= NULL
;
1070 CGroupMask mask
, required_mask
;
1073 required_mask
= c
->type
== CONDITION_MEMORY_PRESSURE
? CGROUP_MASK_MEMORY
:
1074 c
->type
== CONDITION_CPU_PRESSURE
? CGROUP_MASK_CPU
:
1077 slice
= strstrip(first
);
1079 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL
), "Failed to parse condition parameter %s.", c
->parameter
);
1081 r
= cg_mask_supported(&mask
);
1083 return log_debug_errno(r
, "Failed to get supported cgroup controllers: %m");
1085 if (!FLAGS_SET(mask
, required_mask
)) {
1086 log_debug("Cgroup %s controller not available, skipping PSI condition check.", pressure_type
);
1090 r
= cg_slice_to_path(slice
, &slice_path
);
1092 return log_debug_errno(r
, "Cannot determine slice \"%s\" cgroup path: %m", slice
);
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
);
1097 return log_debug_errno(r
, "Failed to get root cgroup path: %m");
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
);
1104 if (!empty_or_root(root_scope
)) {
1105 _cleanup_free_
char *slice_joined
= NULL
;
1107 slice_joined
= path_join(root_scope
, slice_path
);
1109 return log_oom_debug();
1111 free_and_replace(slice_path
, slice_joined
);
1114 r
= cg_get_path(SYSTEMD_CGROUP_CONTROLLER
, slice_path
, controller
, &pressure_path
);
1116 return log_debug_errno(r
, "Error getting cgroup pressure path from %s: %m", slice_path
);
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
);
1126 return log_debug_errno(r
< 0 ? r
: SYNTHETIC_ERRNO(EINVAL
), "Failed to parse condition parameter %s: %m", c
->parameter
);
1128 current
= &pressure
.avg300
;
1130 const char *timespan
;
1132 timespan
= skip_leading_chars(fourth
, NULL
);
1134 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL
), "Failed to parse condition parameter %s.", c
->parameter
);
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
;
1143 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL
), "Failed to parse condition parameter %s.", c
->parameter
);
1146 value
= strstrip(third
);
1148 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL
), "Failed to parse condition parameter %s.", c
->parameter
);
1150 r
= parse_permyriad(value
);
1152 return log_debug_errno(r
, "Failed to parse permyriad: %s", c
->parameter
);
1154 r
= store_loadavg_fixed_point(r
/ 100LU, r
% 100LU, &limit
);
1156 return log_debug_errno(r
, "Failed to parse loadavg: %s", c
->parameter
);
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
);
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
);
1169 return log_debug_errno(r
, "Error parsing pressure from %s: %m", pressure_path
);
1171 return *current
<= limit
;
1174 static int condition_test_kernel_module_loaded(Condition
*c
, char **env
) {
1178 assert(c
->parameter
);
1179 assert(c
->type
== CONDITION_KERNEL_MODULE_LOADED
);
1181 /* Checks whether a specific kernel module is fully loaded (i.e. with the full initialization routine
1184 _cleanup_free_
char *normalized
= strreplace(c
->parameter
, "-", "_");
1186 return log_oom_debug();
1188 if (!filename_is_valid(normalized
)) {
1189 log_debug("Kernel module name '%s' is not valid, hence reporting it to not be loaded.", normalized
);
1193 _cleanup_free_
char *p
= path_join("/sys/module/", normalized
);
1195 return log_oom_debug();
1197 _cleanup_close_
int dir_fd
= open(p
, O_PATH
|O_DIRECTORY
|O_CLOEXEC
);
1199 if (errno
== ENOENT
) {
1200 log_debug_errno(errno
, "'%s/' does not exist, kernel module '%s' not loaded.", p
, normalized
);
1204 return log_debug_errno(errno
, "Failed to open directory '%s/': %m", p
);
1207 _cleanup_free_
char *initstate
= NULL
;
1208 r
= read_virtual_file_at(dir_fd
, "initstate", SIZE_MAX
, &initstate
, NULL
);
1210 log_debug_errno(r
, "'%s/' exists but '%s/initstate' does not, kernel module '%s' is built-in, hence loaded.", p
, p
, normalized
);
1214 return log_debug_errno(r
, "Failed to open '%s/initstate': %m", p
);
1216 delete_trailing_chars(initstate
, WHITESPACE
);
1218 if (!streq(initstate
, "live")) {
1219 log_debug("Kernel module '%s' is reported as '%s', hence not loaded.", normalized
, initstate
);
1223 log_debug("Kernel module '%s' detected as loaded.", normalized
);
1227 int condition_test(Condition
*c
, char **env
) {
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
,
1269 assert(c
->type
>= 0);
1270 assert(c
->type
< _CONDITION_TYPE_MAX
);
1272 r
= condition_tests
[c
->type
](c
, env
);
1274 c
->result
= CONDITION_ERROR
;
1278 b
= (r
> 0) == !c
->negate
;
1279 c
->result
= b
? CONDITION_SUCCEEDED
: CONDITION_FAILED
;
1283 bool condition_test_list(
1286 condition_to_string_t to_string
,
1287 condition_test_logger_t logger
,
1292 /* If the condition list is empty, then it is true */
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
) {
1302 r
= condition_test(c
, env
);
1306 logger(userdata
, LOG_WARNING
, r
, PROJECT_FILE
, __LINE__
, __func__
,
1307 "Couldn't determine result for %s=%s%s%s, assuming failed: %m",
1309 c
->trigger
? "|" : "",
1310 c
->negate
? "!" : "",
1313 logger(userdata
, LOG_DEBUG
, 0, PROJECT_FILE
, __LINE__
, __func__
,
1316 c
->trigger
? "|" : "",
1317 c
->negate
? "!" : "",
1319 condition_result_to_string(c
->result
));
1322 if (!c
->trigger
&& r
<= 0)
1325 if (c
->trigger
&& triggered
<= 0)
1329 return triggered
!= 0;
1332 void condition_dump(Condition
*c
, FILE *f
, const char *prefix
, condition_to_string_t to_string
) {
1337 prefix
= strempty(prefix
);
1340 "%s\t%s: %s%s%s %s\n",
1343 c
->trigger
? "|" : "",
1344 c
->negate
? "!" : "",
1346 condition_result_to_string(c
->result
));
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
);
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",
1391 DEFINE_PRIVATE_STRING_TABLE_LOOKUP(_condition_type
, ConditionType
);
1393 const char* condition_type_to_string(ConditionType t
) {
1394 return _condition_type_to_string(t
);
1397 ConditionType
condition_type_from_string(const char *s
) {
1398 /* for backward compatibility */
1399 if (streq_ptr(s
, "ConditionKernelVersion"))
1400 return CONDITION_VERSION
;
1402 return _condition_type_from_string(s
);
1405 void condition_types_list(void) {
1406 DUMP_STRING_TABLE(_condition_type
, ConditionType
, _CONDITION_TYPE_MAX
);
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",
1446 DEFINE_PRIVATE_STRING_TABLE_LOOKUP(_assert_type
, ConditionType
);
1448 const char* assert_type_to_string(ConditionType t
) {
1449 return _assert_type_to_string(t
);
1452 ConditionType
assert_type_from_string(const char *s
) {
1453 /* for backward compatibility */
1454 if (streq_ptr(s
, "AssertKernelVersion"))
1455 return CONDITION_VERSION
;
1457 return _assert_type_from_string(s
);
1460 void assert_types_list(void) {
1461 DUMP_STRING_TABLE(_assert_type
, ConditionType
, _CONDITION_TYPE_MAX
);
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",
1471 DEFINE_STRING_TABLE_LOOKUP(condition_result
, ConditionResult
);