Add support for conditions on the machines firmware

This allows to limit units to machines that run on a certain firmware
type. For device tree defined machines checking against the machine's
compatible is also possible.
This commit is contained in:
Uwe Kleine-König
2021-04-28 10:55:55 +02:00
committed by Lennart Poettering
parent 192a9d95ea
commit cbcdcaaa0e
12 changed files with 120 additions and 0 deletions
+79
View File
@@ -5,6 +5,7 @@
#include <fnmatch.h>
#include <limits.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include <time.h>
@@ -450,6 +451,81 @@ static int condition_test_architecture(Condition *c, char **env) {
return a == b;
}
#define DTCOMPAT_FILE "/sys/firmware/devicetree/base/compatible"
static int condition_test_firmware_devicetree_compatible(const char *dtcarg) {
int r;
_cleanup_free_ char *dtcompat = NULL;
_cleanup_strv_free_ char **dtcompatlist = NULL;
size_t size;
r = read_full_virtual_file(DTCOMPAT_FILE, &dtcompat, &size);
if (r < 0) {
/* if the path doesn't exist it is incompatible */
if (r != -ENOENT)
log_debug_errno(r, "Failed to open() '%s', assuming machine is incompatible: %m", DTCOMPAT_FILE);
return false;
}
/* Not sure this can happen, but play safe. */
if (size == 0) {
log_debug("%s has zero length, assuming machine is incompatible", DTCOMPAT_FILE);
return false;
}
/*
* /sys/firmware/devicetree/base/compatible consists of one or more
* strings, each ending in '\0'. So the last character in dtcompat must
* be a '\0'.
*/
if (dtcompat[size - 1] != '\0') {
log_debug("%s is in an unknown format, assuming machine is incompatible", DTCOMPAT_FILE);
return false;
}
dtcompatlist = strv_parse_nulstr(dtcompat, size);
if (!dtcompatlist)
return -ENOMEM;
return strv_contains(dtcompatlist, dtcarg);
}
static int condition_test_firmware(Condition *c, char **env) {
sd_char *dtc;
assert(c);
assert(c->parameter);
assert(c->type == CONDITION_FIRMWARE);
if (streq(c->parameter, "device-tree")) {
if (access("/sys/firmware/device-tree/", F_OK) < 0) {
if (errno != ENOENT)
log_debug_errno(errno, "Unexpected error when checking for /sys/firmware/device-tree/: %m");
return false;
} else
return true;
} else if ((dtc = startswith(c->parameter, "device-tree-compatible("))) {
_cleanup_free_ char *dtcarg = NULL;
char *end;
end = strchr(dtc, ')');
if (!end || *(end + 1) != '\0') {
log_debug("Malformed Firmware condition \"%s\"", c->parameter);
return false;
}
dtcarg = strndup(dtc, end - dtc);
if (!dtcarg)
return -ENOMEM;
return condition_test_firmware_devicetree_compatible(dtcarg);
} else if (streq(c->parameter, "uefi"))
return is_efi_boot();
else {
log_debug("Unsupported Firmware condition \"%s\"", c->parameter);
return false;
}
}
static int condition_test_host(Condition *c, char **env) {
_cleanup_free_ char *h = NULL;
sd_id128_t x, y;
@@ -843,6 +919,7 @@ int condition_test(Condition *c, char **env) {
[CONDITION_HOST] = condition_test_host,
[CONDITION_AC_POWER] = condition_test_ac_power,
[CONDITION_ARCHITECTURE] = condition_test_architecture,
[CONDITION_FIRMWARE] = condition_test_firmware,
[CONDITION_NEEDS_UPDATE] = condition_test_needs_update,
[CONDITION_FIRST_BOOT] = condition_test_first_boot,
[CONDITION_USER] = condition_test_user,
@@ -949,6 +1026,7 @@ void condition_dump_list(Condition *first, FILE *f, const char *prefix, conditio
static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
[CONDITION_ARCHITECTURE] = "ConditionArchitecture",
[CONDITION_FIRMWARE] = "ConditionFirmware",
[CONDITION_VIRTUALIZATION] = "ConditionVirtualization",
[CONDITION_HOST] = "ConditionHost",
[CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine",
@@ -981,6 +1059,7 @@ DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType);
static const char* const assert_type_table[_CONDITION_TYPE_MAX] = {
[CONDITION_ARCHITECTURE] = "AssertArchitecture",
[CONDITION_FIRMWARE] = "AssertFirmware",
[CONDITION_VIRTUALIZATION] = "AssertVirtualization",
[CONDITION_HOST] = "AssertHost",
[CONDITION_KERNEL_COMMAND_LINE] = "AssertKernelCommandLine",