diff options
author | Lennart Poettering <lennart@poettering.net> | 2023-11-10 16:11:12 +0100 |
---|---|---|
committer | Luca Boccassi <luca.boccassi@gmail.com> | 2023-11-13 20:32:34 +0100 |
commit | abc19a6ffaa94893ffc40cc000e5bb4437f67656 (patch) | |
tree | 5566c41ae2d45f9bc66f76a24f1fd513b7e8a8db /src/storagetm | |
parent | Merge pull request #30016 from dtardon/udevadm-control-p-test (diff) | |
download | systemd-abc19a6ffaa94893ffc40cc000e5bb4437f67656.tar.xz systemd-abc19a6ffaa94893ffc40cc000e5bb4437f67656.zip |
storagetm: expose more useful metadata for nvme block devices
don't let the devices to be announced just as model "Linux". Let's instead
propagate the underlying block device's model. Also do something
reasonably smart for the serial and firmware version fields.
Diffstat (limited to 'src/storagetm')
-rw-r--r-- | src/storagetm/storagetm.c | 152 |
1 files changed, 148 insertions, 4 deletions
diff --git a/src/storagetm/storagetm.c b/src/storagetm/storagetm.c index 8d36184b76..c9f6dd9214 100644 --- a/src/storagetm/storagetm.c +++ b/src/storagetm/storagetm.c @@ -13,9 +13,11 @@ #include "fileio.h" #include "format-util.h" #include "fs-util.h" +#include "id128-util.h" #include "local-addresses.h" #include "loop-util.h" #include "main-func.h" +#include "os-util.h" #include "parse-argument.h" #include "path-util.h" #include "plymouth-util.h" @@ -220,7 +222,137 @@ static NvmeSubsystem *nvme_subsystem_destroy(NvmeSubsystem *s) { DEFINE_TRIVIAL_CLEANUP_FUNC(NvmeSubsystem*, nvme_subsystem_destroy); -static int nvme_subsystem_add(const char *node, int consumed_fd, NvmeSubsystem **ret) { +static int nvme_subsystem_write_metadata(int subsystem_fd, sd_device *device) { + _cleanup_free_ char *image_id = NULL, *image_version = NULL, *os_id = NULL, *os_version = NULL, *combined_model = NULL, *synthetic_serial = NULL; + const char *hwmodel = NULL, *hwserial = NULL, *w; + int r; + + assert(subsystem_fd >= 0); + + (void) parse_os_release( + /* root= */ NULL, + "IMAGE_ID", &image_id, + "IMAGE_VERSION", &image_version, + "ID", &os_id, + "VERSION_ID", &os_version); + + if (device) { + (void) device_get_model_string(device, &hwmodel); + (void) sd_device_get_property_value(device, "ID_SERIAL_SHORT", &hwserial); + } + + w = secure_getenv("SYSTEMD_NVME_MODEL"); + if (!w) { + if (hwmodel && (image_id || os_id)) { + if (asprintf(&combined_model, "%s (%s)", hwmodel, image_id ?: os_id) < 0) + return log_oom(); + w = combined_model; + } else + w = hwmodel ?: image_id ?: os_id; + } + if (w) { + _cleanup_free_ char *truncated = strndup(w, 40); /* kernel refuses more than 40 chars (as per nvme spec) */ + + /* The default string stored in 'attr_model' is "Linux" btw. */ + r = write_string_file_at(subsystem_fd, "attr_model", truncated, WRITE_STRING_FILE_DISABLE_BUFFER); + if (r < 0) + log_warning_errno(r, "Failed to set model of subsystem to '%s', ignoring: %m", w); + } + + w = secure_getenv("SYSTEMD_NVME_FIRMWARE"); + if (!w) + w = image_version ?: os_version; + if (w) { + _cleanup_free_ char *truncated = strndup(w, 8); /* kernel refuses more than 8 chars (as per nvme spec) */ + if (!truncated) + return log_oom(); + + /* The default string stored in 'attr_firmware' is `uname -r` btw, but truncated to 8 chars. */ + r = write_string_file_at(subsystem_fd, "attr_firmware", truncated, WRITE_STRING_FILE_DISABLE_BUFFER); + if (r < 0) + log_warning_errno(r, "Failed to set model of subsystem to '%s', ignoring: %m", truncated); + } + + w = secure_getenv("SYSTEMD_NVME_SERIAL"); + if (!w) { + if (hwserial) + w = hwserial; + else { + sd_id128_t mid; + + r = sd_id128_get_machine_app_specific(SD_ID128_MAKE(39,7f,4d,bf,1e,bf,46,6d,b3,cb,45,b8,0d,49,5b,c1), &mid); + if (r < 0) + log_warning_errno(r, "Failed to get machine ID, ignoring: %m"); + else { + if (asprintf(&synthetic_serial, SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(mid)) < 0) + return log_oom(); + w = synthetic_serial; + } + } + } + if (w) { + _cleanup_free_ char *truncated = strndup(w, 20); /* kernel refuses more than 20 chars (as per nvme spec) */ + if (!truncated) + return log_oom(); + + r = write_string_file_at(subsystem_fd, "attr_serial", truncated, WRITE_STRING_FILE_DISABLE_BUFFER); + if (r < 0) + log_warning_errno(r, "Failed to set serial of subsystem to '%s', ignoring: %m", truncated); + } + + return 0; +} + +static int nvme_namespace_write_metadata(int namespace_fd, sd_device *device, const char *node) { + sd_id128_t id = SD_ID128_NULL; + const char *e; + int r; + + assert(namespace_fd >= 0); + + e = secure_getenv("SYSTEMD_NVME_UUID"); + if (e) { + r = sd_id128_from_string(e, &id); + if (r < 0) + log_warning_errno(r, "Failed to parse $SYSTEMD_NVME_UUID, ignoring: %s", e); + } + + if (sd_id128_is_null(id)) { + const char *serial = NULL; + sd_id128_t mid = SD_ID128_NULL; + + /* We combine machine ID and ID_SERIAL and hash a UUID from it */ + + if (device) { + (void) sd_device_get_property_value(device, "ID_SERIAL", &serial); + if (!serial) + sd_device_get_devpath(device, &serial); + } else + serial = node; + + r = sd_id128_get_machine(&mid); + if (r < 0) + log_warning_errno(r, "Failed to get machine ID, ignoring: %m"); + + size_t l = sizeof(mid) + strlen_ptr(serial); + _cleanup_free_ void *j = malloc(l + 1); + if (!j) + return log_oom(); + + strcpy(mempcpy(j, &mid, sizeof(mid)), strempty(serial)); + + id = id128_digest(j, l); + } + + r = write_string_file_at(namespace_fd, "device_uuid", SD_ID128_TO_UUID_STRING(id), WRITE_STRING_FILE_DISABLE_BUFFER); + if (r < 0) + log_warning_errno(r, "Failed to set uuid of namespace to '%s', ignoring: %m", SD_ID128_TO_UUID_STRING(id)); + + return 0; +} + +static int nvme_subsystem_add(const char *node, int consumed_fd, sd_device *device, NvmeSubsystem **ret) { + _cleanup_(sd_device_unrefp) sd_device *allocated_device = NULL; _cleanup_close_ int fd = consumed_fd; /* always take possession of the fd */ int r; @@ -246,7 +378,15 @@ static int nvme_subsystem_add(const char *node, int consumed_fd, NvmeSubsystem * struct stat st; if (fstat(fd, &st) < 0) return log_error_errno(errno, "Failed to fstat '%s': %m", node); - if (!S_ISBLK(st.st_mode)) { + if (S_ISBLK(st.st_mode)) { + if (!device) { + r = sd_device_new_from_devnum(&allocated_device, 'b', st.st_dev); + if (r < 0) + return log_error_errno(r, "Failed to get device information for device '%s': %m", node); + + device = allocated_device; + } + } else { r = stat_verify_regular(&st); if (r < 0) return log_error_errno(r, "Not a block device or regular file, refusing: %s", node); @@ -271,11 +411,15 @@ static int nvme_subsystem_add(const char *node, int consumed_fd, NvmeSubsystem * if (r < 0) return log_error_errno(r, "Failed to set 'attr_allow_any_host' flag: %m"); + (void) nvme_subsystem_write_metadata(subsystem_fd, device); + _cleanup_close_ int namespace_fd = -EBADF; namespace_fd = open_mkdir_at(subsystem_fd, "namespaces/1", O_EXCL|O_RDONLY|O_CLOEXEC, 0777); if (namespace_fd < 0) return log_error_errno(namespace_fd, "Failed to create NVME namespace '1': %m"); + (void) nvme_namespace_write_metadata(namespace_fd, device, node); + /* We use /proc/$PID/fd/$FD rather than /proc/self/fd/$FD, because this string is visible to others * via configfs, and by including the PID it's clear to who the stuff belongs. */ r = write_string_file_at(namespace_fd, "device_path", FORMAT_PROC_PID_FD_PATH(0, fd), WRITE_STRING_FILE_DISABLE_BUFFER); @@ -826,7 +970,7 @@ static int device_added(Context *c, sd_device *device) { } _cleanup_(nvme_subsystem_destroyp) NvmeSubsystem *s = NULL; - r = nvme_subsystem_add(devname, TAKE_FD(fd), &s); + r = nvme_subsystem_add(devname, TAKE_FD(fd), device, &s); if (r < 0) return r; @@ -974,7 +1118,7 @@ static int run(int argc, char* argv[]) { STRV_FOREACH(i, arg_devices) { _cleanup_(nvme_subsystem_destroyp) NvmeSubsystem *subsys = NULL; - r = nvme_subsystem_add(*i, -EBADF, &subsys); + r = nvme_subsystem_add(*i, -EBADF, /* device= */ NULL, &subsys); if (r < 0) return r; |