lvm2/SOURCES/0008-Allow-system.devices-to-be-automatically-created-on-.patch

579 lines
19 KiB
Diff

From 16af82a5afe337cc3b0db3eaed0372e130b049ed Mon Sep 17 00:00:00 2001
From: David Teigland <teigland@redhat.com>
Date: Tue, 23 Apr 2024 17:08:26 -0500
Subject: [PATCH 08/13] Allow system.devices to be automatically created on
first boot
An OS installer can create system.devices for the system and
disks, but an OS image cannot create the system-specific
system.devices. The OS image can instead configure the
image so that lvm will create system.devices on first boot.
Image preparation steps to enable auto creation of system.devices:
- create empty file /etc/lvm/devices/auto-import-rootvg
- remove any existing /etc/lvm/devices/system.devices
- enable lvm-devices-import.path
- enable lvm-devices-import.service
On first boot of the prepared image:
- udev triggers vgchange -aay --autoactivation event <rootvg>
- vgchange activates LVs in the root VG
- vgchange finds the file /etc/lvm/devices/auto-import-rootvg,
and no /etc/lvm/devices/system.devices, so it creates
/run/lvm/lvm-devices-import
- lvm-devices-import.path is run when /run/lvm/lvm-devices-import
appears, and triggers lvm-devices-import.service
- lvm-devices-import.service runs vgimportdevices --rootvg --auto
- vgimportdevices finds /etc/lvm/devices/auto-import-rootvg,
and no system.devices, so it creates system.devices containing
PVs in the root VG, and removes /etc/lvm/devices/auto-import-rootvg
and /run/lvm/lvm-devices-import
Run directly, vgimportdevices --rootvg (without --auto), will create
a new system.devices for the root VG, or will add devices for the
root VG to an existing system.devices.
(cherry picked from commit c609dedc2f035f770b5f645c4695924abf15c2ca)
(cherry picked from commit 3321a669d8f2df99df9d6dcd4ebb2b4d30731c7a)
---
lib/commands/toolcontext.h | 1 +
lib/config/defaults.h | 2 +
lib/device/device_id.c | 5 +-
scripts/lvm-devices-import.path | 12 +++
scripts/lvm-devices-import.service | 12 +++
tools/args.h | 11 ++
tools/command-lines.in | 5 +
tools/pvscan.c | 4 +-
tools/tools.h | 2 +-
tools/vgchange.c | 155 ++++++++++++++++++++++++++++-
tools/vgimportdevices.c | 114 ++++++++++++++++++++-
11 files changed, 315 insertions(+), 8 deletions(-)
create mode 100644 scripts/lvm-devices-import.path
create mode 100644 scripts/lvm-devices-import.service
diff --git a/lib/commands/toolcontext.h b/lib/commands/toolcontext.h
index fec0a52cf..043afbf76 100644
--- a/lib/commands/toolcontext.h
+++ b/lib/commands/toolcontext.h
@@ -217,6 +217,7 @@ struct cmd_context {
unsigned device_ids_check_hostname:1;
unsigned device_ids_refresh_trigger:1;
unsigned device_ids_invalid:1;
+ unsigned device_ids_auto_import:1;
unsigned get_vgname_from_options:1; /* used by lvconvert */
/*
diff --git a/lib/config/defaults.h b/lib/config/defaults.h
index ed0c4f404..efe36d1fa 100644
--- a/lib/config/defaults.h
+++ b/lib/config/defaults.h
@@ -337,6 +337,8 @@
#define VGS_ONLINE_DIR DEFAULT_RUN_DIR "/vgs_online"
#define PVS_LOOKUP_DIR DEFAULT_RUN_DIR "/pvs_lookup"
+#define DEVICES_IMPORT_PATH DEFAULT_RUN_DIR "/lvm-devices-import"
+
#define DEFAULT_DEVICE_ID_SYSFS_DIR "/sys/" /* trailing / to match dm_sysfs_dir() */
#define DEFAULT_DEVICESFILE_BACKUP_LIMIT 50
diff --git a/lib/device/device_id.c b/lib/device/device_id.c
index 2b183810a..1ce7927ed 100644
--- a/lib/device/device_id.c
+++ b/lib/device/device_id.c
@@ -1726,9 +1726,10 @@ int device_ids_write(struct cmd_context *cmd)
if ((fc_bytes = snprintf(fc, sizeof(fc),
"# LVM uses devices listed in this file.\n" \
- "# Created by LVM command %s pid %d at %s" \
+ "# Created by LVM command %s%s pid %d at %s" \
"# HASH=%u\n",
- cmd->name, getpid(), ctime(&t), hash)) < 0) {
+ cmd->name, cmd->device_ids_auto_import ? " (auto)" : "",
+ getpid(), ctime(&t), hash)) < 0) {
log_error("Failed to write buffer for devices file content.");
goto out;
}
diff --git a/scripts/lvm-devices-import.path b/scripts/lvm-devices-import.path
new file mode 100644
index 000000000..bcf0dcd4c
--- /dev/null
+++ b/scripts/lvm-devices-import.path
@@ -0,0 +1,12 @@
+[Unit]
+Description=lvm-devices-import to create system.devices
+
+# /run/lvm/lvm-devices-import created by vgchange -aay <rootvg>
+
+[Path]
+PathExists=/run/lvm/lvm-devices-import
+Unit=lvm-devices-import.service
+ConditionPathExists=!/etc/lvm/devices/system.devices
+
+[Install]
+WantedBy=multi-user.target
diff --git a/scripts/lvm-devices-import.service b/scripts/lvm-devices-import.service
new file mode 100644
index 000000000..9d3bda2ee
--- /dev/null
+++ b/scripts/lvm-devices-import.service
@@ -0,0 +1,12 @@
+[Unit]
+Description=Create lvm system.devices
+
+[Service]
+Type=oneshot
+RemainAfterExit=no
+ExecStart=/usr/sbin/vgimportdevices --rootvg --auto
+ConditionPathExists=!/etc/lvm/devices/system.devices
+
+[Install]
+WantedBy=multi-user.target
+
diff --git a/tools/args.h b/tools/args.h
index 09b2ad551..ed0fb9620 100644
--- a/tools/args.h
+++ b/tools/args.h
@@ -94,6 +94,14 @@ arg(atversion_ARG, '\0', "atversion", string_VAL, 0, 0,
"which does not contain any newer settings for which LVM would\n"
"issue a warning message when checking the configuration.\n")
+arg(auto_ARG, '\0', "auto", 0, 0, 0,
+ "This option is used when automatically importing devices for the root VG.\n"
+ "The auto import is intended to be done once, on first boot, to create an\n"
+ "initial system.devices file for the root VG.\n"
+ "When this option is used, the vgimportdevices --rootvg command does nothing\n"
+ "if system.devices exists, or the file auto-import-rootvg does not exist\n"
+ "(both in the /etc/lvm/devices/ directory.)\n")
+
arg(autoactivation_ARG, '\0', "autoactivation", string_VAL, 0, 0,
"Specify if autoactivation is being used from an event.\n"
"This allows the command to apply settings that are specific\n"
@@ -754,6 +762,9 @@ arg(resync_ARG, '\0', "resync", 0, 0, 0,
"which the LV is without a complete redundant copy of the data.\n"
"See \\fBlvmraid\\fP(7) for more information.\n")
+arg(rootvg_ARG, '\0', "rootvg", 0, 0, 0,
+ "Import devices used for the root VG.\n")
+
arg(rows_ARG, '\0', "rows", 0, 0, 0,
"Output columns as rows.\n")
diff --git a/tools/command-lines.in b/tools/command-lines.in
index 1c728afa0..3ad5d3c46 100644
--- a/tools/command-lines.in
+++ b/tools/command-lines.in
@@ -1911,6 +1911,11 @@ OO: --foreign, --reportformat ReportFmt
ID: vgimportdevices_all
DESC: Add devices from all accessible VGs to the devices file.
+vgimportdevices --rootvg
+OO: --auto, --reportformat ReportFmt
+ID: vgimportdevices_root
+DESC: Add devices from root VG to the devices file.
+
---
vgmerge VG VG
diff --git a/tools/pvscan.c b/tools/pvscan.c
index f88e1b751..0a9cb59df 100644
--- a/tools/pvscan.c
+++ b/tools/pvscan.c
@@ -495,7 +495,7 @@ static int _pvscan_aa_single(struct cmd_context *cmd, const char *vg_name,
log_debug("pvscan autoactivating VG %s.", vg_name);
- if (!vgchange_activate(cmd, vg, CHANGE_AAY, 1)) {
+ if (!vgchange_activate(cmd, vg, CHANGE_AAY, 1, NULL)) {
log_error_pvscan(cmd, "%s: autoactivation failed.", vg->name);
pp->activate_errors++;
}
@@ -755,7 +755,7 @@ static int _pvscan_aa_quick(struct cmd_context *cmd, struct pvscan_aa_params *pp
log_debug("pvscan autoactivating VG %s.", vgname);
- if (!vgchange_activate(cmd, vg, CHANGE_AAY, 1)) {
+ if (!vgchange_activate(cmd, vg, CHANGE_AAY, 1, NULL)) {
log_error_pvscan(cmd, "%s: autoactivation failed.", vg->name);
pp->activate_errors++;
}
diff --git a/tools/tools.h b/tools/tools.h
index f4a0c94d7..58708c695 100644
--- a/tools/tools.h
+++ b/tools/tools.h
@@ -164,7 +164,7 @@ int mirror_remove_missing(struct cmd_context *cmd,
int vgchange_activate(struct cmd_context *cmd, struct volume_group *vg,
- activation_change_t activate, int vg_complete_to_activate);
+ activation_change_t activate, int vg_complete_to_activate, char *root_dm_uuid);
int vgchange_background_polling(struct cmd_context *cmd, struct volume_group *vg);
diff --git a/tools/vgchange.c b/tools/vgchange.c
index 378ded16e..2004d6e92 100644
--- a/tools/vgchange.c
+++ b/tools/vgchange.c
@@ -16,11 +16,14 @@
#include "tools.h"
#include "lib/device/device_id.h"
#include "lib/label/hints.h"
+#include "device_mapper/misc/dm-ioctl.h"
+#include <mntent.h>
struct vgchange_params {
int lock_start_count;
unsigned int lock_start_sanlock : 1;
unsigned int vg_complete_to_activate : 1;
+ char *root_dm_uuid; /* dm uuid of LV under root fs */
};
/*
@@ -197,7 +200,7 @@ int vgchange_background_polling(struct cmd_context *cmd, struct volume_group *vg
}
int vgchange_activate(struct cmd_context *cmd, struct volume_group *vg,
- activation_change_t activate, int vg_complete_to_activate)
+ activation_change_t activate, int vg_complete_to_activate, char *root_dm_uuid)
{
int lv_open, active, monitored = 0, r = 1;
const struct lv_list *lvl;
@@ -279,6 +282,43 @@ int vgchange_activate(struct cmd_context *cmd, struct volume_group *vg,
r = 0;
}
+ /*
+ * Possibly trigger auto-generation of system.devices:
+ * - if root_dm_uuid contains vg->id, and
+ * - /etc/lvm/devices/auto-import-rootvg exists, and
+ * - /etc/lvm/devices/system.devices does not exist, then
+ * - create /run/lvm/lvm-devices-import to
+ * trigger lvm-devices-import.path and .service
+ * - lvm-devices-import will run vgimportdevices --rootvg
+ * to create system.devices
+ */
+ if (root_dm_uuid) {
+ char path[PATH_MAX];
+ struct stat info;
+ FILE *fp;
+
+ if (memcmp(root_dm_uuid + 4, &vg->id, ID_LEN))
+ goto out;
+
+ if (cmd->enable_devices_file || devices_file_exists(cmd))
+ goto out;
+
+ if (dm_snprintf(path, sizeof(path), "%s/devices/auto-import-rootvg", cmd->system_dir) < 0)
+ goto out;
+
+ if (stat(path, &info) < 0)
+ goto out;
+
+ log_debug("Found %s creating %s", path, DEVICES_IMPORT_PATH);
+
+ if (!(fp = fopen(DEVICES_IMPORT_PATH, "w"))) {
+ log_debug("failed to create %s", DEVICES_IMPORT_PATH);
+ goto out;
+ }
+ if (fclose(fp))
+ stack;
+ }
+out:
/* Print message only if there was not found a missing VG */
log_print_unless_silent("%d logical volume(s) in volume group \"%s\" now active",
lvs_in_vg_activated(vg), vg->name);
@@ -714,7 +754,7 @@ static int _vgchange_single(struct cmd_context *cmd, const char *vg_name,
if (arg_is_set(cmd, activate_ARG)) {
activate = (activation_change_t) arg_uint_value(cmd, activate_ARG, 0);
- if (!vgchange_activate(cmd, vg, activate, vp->vg_complete_to_activate))
+ if (!vgchange_activate(cmd, vg, activate, vp->vg_complete_to_activate, vp->root_dm_uuid))
return_ECMD_FAILED;
} else if (arg_is_set(cmd, refresh_ARG)) {
/* refreshes the visible LVs (which starts polling) */
@@ -735,6 +775,115 @@ static int _vgchange_single(struct cmd_context *cmd, const char *vg_name,
return ret;
}
+/*
+ * Automatic creation of system.devices for root VG on first boot
+ * is useful for OS images where the OS installer is not used to
+ * customize the OS for system.
+ *
+ * - OS image prep:
+ * . rm /etc/lvm/devices/system.devices (if it exists)
+ * . touch /etc/lvm/devices/auto-import-rootvg
+ * . enable lvm-devices-import.path
+ * . enable lvm-devices-import.service
+ *
+ * - lvchange -ay <rootvg>/<rootlv>
+ * . run by initrd so root fs can be mounted
+ * . does not use system.devices
+ * . named <rootvg>/<rootlv> comes from kernel command line rd.lvm
+ * . uses first device that appears containing the named root LV
+ *
+ * - vgchange -aay <rootvg>
+ * . triggered by udev when all PVs from root VG are online
+ * . activate LVs in root VG (in addition to the already active root LV)
+ * . check for /etc/lvm/devices/auto-import-rootvg (found)
+ * . check for /etc/lvm/devices/system.devices (not found)
+ * . create /run/lvm/lvm-devices-import because
+ * auto-import-rootvg was found and system.devices was not found
+ *
+ * - lvm-devices-import.path
+ * . triggered by /run/lvm/lvm-devices-import
+ * . start lvm-devices-import.service
+ *
+ * - lvm-devices-import.service
+ * . check for /etc/lvm/devices/system.devices, do nothing if found
+ * . run vgimportdevices --rootvg --auto
+ *
+ * - vgimportdevices --rootvg --auto
+ * . check for /etc/lvm/devices/auto-import-rootvg (found)
+ * . check for /etc/lvm/devices/system.devices (not found)
+ * . creates /etc/lvm/devices/system.devices for PVs in root VG
+ * . removes /etc/lvm/devices/auto-import-rootvg
+ * . removes /run/lvm/lvm-devices-import
+ *
+ * On future startup, /etc/lvm/devices/system.devices will exist,
+ * and /etc/lvm/devices/auto-import-rootvg will not exist, so
+ * vgchange -aay <rootvg> will not create /run/lvm/lvm-devices-import,
+ * and lvm-devices-import.path and lvm-device-import.service will not run.
+ *
+ * lvm-devices-import.path:
+ * [Path]
+ * PathExists=/run/lvm/lvm-devices-import
+ * Unit=lvm-devices-import.service
+ * ConditionPathExists=!/etc/lvm/devices/system.devices
+ *
+ * lvm-devices-import.service:
+ * [Service]
+ * Type=oneshot
+ * RemainAfterExit=no
+ * ExecStart=/usr/sbin/vgimportdevices --rootvg --auto
+ * ConditionPathExists=!/etc/lvm/devices/system.devices
+ */
+
+static void _get_rootvg_dev(struct cmd_context *cmd, char **dm_uuid_out)
+{
+ char path[PATH_MAX];
+ char dm_uuid[DM_UUID_LEN];
+ struct stat info;
+ FILE *fme = NULL;
+ struct mntent *me;
+ int found = 0;
+
+ if (cmd->enable_devices_file || devices_file_exists(cmd))
+ return;
+
+ if (dm_snprintf(path, sizeof(path), "%s/devices/auto-import-rootvg", cmd->system_dir) < 0)
+ return;
+
+ if (stat(path, &info) < 0)
+ return;
+
+ if (!(fme = setmntent("/etc/mtab", "r")))
+ return;
+
+ while ((me = getmntent(fme))) {
+ if ((me->mnt_dir[0] == '/') && (me->mnt_dir[1] == '\0')) {
+ found = 1;
+ break;
+ }
+ }
+ endmntent(fme);
+
+ if (!found)
+ return;
+
+ if (stat(me->mnt_dir, &info) < 0)
+ return;
+
+ if (!get_dm_uuid_from_sysfs(dm_uuid, sizeof(dm_uuid), (int)MAJOR(info.st_dev), (int)MINOR(info.st_dev)))
+ return;
+
+ log_debug("Found root dm_uuid %s", dm_uuid);
+
+ /* UUID_PREFIX = "LVM-" */
+ if (strncmp(dm_uuid, UUID_PREFIX, 4))
+ return;
+
+ if (strlen(dm_uuid) < 4 + ID_LEN)
+ return;
+
+ *dm_uuid_out = dm_pool_strdup(cmd->mem, dm_uuid);
+}
+
static int _vgchange_autoactivation_setup(struct cmd_context *cmd,
struct vgchange_params *vp,
int *skip_command,
@@ -778,6 +927,8 @@ static int _vgchange_autoactivation_setup(struct cmd_context *cmd,
get_single_vgname_cmd_arg(cmd, NULL, &vgname);
+ _get_rootvg_dev(cmd, &vp->root_dm_uuid);
+
/*
* Lock the VG before scanning the PVs so _vg_read can avoid the normal
* lock_vol+rescan (READ_WITHOUT_LOCK avoids the normal lock_vol and
diff --git a/tools/vgimportdevices.c b/tools/vgimportdevices.c
index bccd94f61..70d12e500 100644
--- a/tools/vgimportdevices.c
+++ b/tools/vgimportdevices.c
@@ -15,11 +15,16 @@
#include "tools.h"
#include "lib/cache/lvmcache.h"
#include "lib/device/device_id.h"
+#include "device_mapper/misc/dm-ioctl.h"
/* coverity[unnecessary_header] needed for MuslC */
#include <sys/file.h>
+#include <mntent.h>
struct vgimportdevices_params {
uint32_t added_devices;
+ int root_vg_found;
+ char *root_dm_uuid;
+ char *root_vg_name;
};
static int _vgimportdevices_single(struct cmd_context *cmd,
@@ -35,6 +40,13 @@ static int _vgimportdevices_single(struct cmd_context *cmd,
int updated_pvs = 0;
const char *idtypestr = NULL; /* deviceidtype_ARG ? */
+ if (vp->root_dm_uuid) {
+ if (memcmp(vp->root_dm_uuid + 4, &vg->id, ID_LEN))
+ return ECMD_PROCESSED;
+ vp->root_vg_found = 1;
+ vp->root_vg_name = dm_pool_strdup(cmd->mem, vg_name);
+ }
+
dm_list_iterate_items(pvl, &vg->pvs) {
if (is_missing_pv(pvl->pv) || !pvl->pv->dev) {
memcpy(pvid, &pvl->pv->id.uuid, ID_LEN);
@@ -86,6 +98,87 @@ static int _vgimportdevices_single(struct cmd_context *cmd,
return ECMD_PROCESSED;
}
+static int _get_rootvg_dev(struct cmd_context *cmd, char **dm_uuid_out, int *skip)
+{
+ char path[PATH_MAX];
+ char dm_uuid[DM_UUID_LEN];
+ struct stat info;
+ FILE *fme = NULL;
+ struct mntent *me;
+ int found = 0;
+
+ /*
+ * When --auto is set, the command does nothing
+ * if /etc/lvm/devices/system.devices exists, or
+ * if /etc/lvm/devices/auto-import-rootvg does not exist.
+ */
+ if (arg_is_set(cmd, auto_ARG)) {
+ if (devices_file_exists(cmd)) {
+ *skip = 1;
+ return 1;
+ }
+
+ if (dm_snprintf(path, sizeof(path), "%s/devices/auto-import-rootvg", cmd->system_dir) < 0)
+ return_0;
+
+ if (stat(path, &info) < 0) {
+ *skip = 1;
+ return 1;
+ }
+
+ /*
+ * This flag is just used in device_ids_write to enable
+ * an extra comment in system.devices indicating that
+ * the file was auto generated for the root vg.
+ */
+ cmd->device_ids_auto_import = 1;
+ }
+
+ if (!(fme = setmntent("/etc/mtab", "r")))
+ return_0;
+
+ while ((me = getmntent(fme))) {
+ if ((me->mnt_dir[0] == '/') && (me->mnt_dir[1] == '\0')) {
+ found = 1;
+ break;
+ }
+ }
+ endmntent(fme);
+
+ if (!found)
+ return_0;
+
+ if (stat(me->mnt_dir, &info) < 0)
+ return_0;
+
+ if (!get_dm_uuid_from_sysfs(dm_uuid, sizeof(dm_uuid), (int)MAJOR(info.st_dev), (int)MINOR(info.st_dev)))
+ return_0;
+
+ /* UUID_PREFIX = "LVM-" */
+ if (strncmp(dm_uuid, UUID_PREFIX, 4))
+ return_0;
+
+ if (strlen(dm_uuid) < 4 + ID_LEN)
+ return_0;
+
+ *dm_uuid_out = dm_pool_strdup(cmd->mem, dm_uuid);
+ return 1;
+}
+
+static void _clear_rootvg_auto(struct cmd_context *cmd)
+{
+ char path[PATH_MAX];
+
+ if (dm_snprintf(path, sizeof(path), "%s/devices/auto-import-rootvg", cmd->system_dir) < 0)
+ return;
+
+ if (unlink(path) < 0)
+ log_debug("Failed to unlink %s", path);
+
+ if (unlink(DEVICES_IMPORT_PATH) < 0)
+ log_debug("Failed to unlink %s", DEVICES_IMPORT_PATH);
+}
+
/*
* This command always scans all devices on the system,
* any pre-existing devices_file does not limit the scope.
@@ -130,6 +223,19 @@ int vgimportdevices(struct cmd_context *cmd, int argc, char **argv)
/* So that we can warn about this. */
cmd->handles_missing_pvs = 1;
+ /* Import devices for the root VG. */
+ if (arg_is_set(cmd, rootvg_ARG)) {
+ int skip = 0;
+ if (!_get_rootvg_dev(cmd, &vp.root_dm_uuid, &skip)) {
+ log_error("Failed to find root VG.");
+ return ECMD_FAILED;
+ }
+ if (skip) {
+ log_print("Root VG auto import is not enabled.");
+ return ECMD_PROCESSED;
+ }
+ }
+
if (!lock_global(cmd, "ex"))
return ECMD_FAILED;
@@ -230,7 +336,13 @@ int vgimportdevices(struct cmd_context *cmd, int argc, char **argv)
goto out;
}
- log_print("Added %u devices to devices file.", vp.added_devices);
+ if (vp.root_vg_found)
+ log_print("Added %u devices to devices file for root VG %s.", vp.added_devices, vp.root_vg_name);
+ else
+ log_print("Added %u devices to devices file.", vp.added_devices);
+
+ if (vp.root_vg_found && arg_is_set(cmd, auto_ARG))
+ _clear_rootvg_auto(cmd);
out:
if ((ret == ECMD_FAILED) && created_file)
if (unlink(cmd->devices_file_path) < 0)
--
2.45.2