summaryrefslogtreecommitdiff
path: root/tools/toollib.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/toollib.c')
-rw-r--r--tools/toollib.c405
1 files changed, 404 insertions, 1 deletions
diff --git a/tools/toollib.c b/tools/toollib.c
index 728e61d4d..fd43d95dc 100644
--- a/tools/toollib.c
+++ b/tools/toollib.c
@@ -3289,6 +3289,7 @@ int process_each_pv(struct cmd_context *cmd,
int argc, char **argv,
const char *only_this_vgname,
uint32_t read_flags,
+ uint32_t each_flags,
struct processing_handle *handle,
process_single_pv_fn_t process_single_pv)
{
@@ -3343,7 +3344,7 @@ int process_each_pv(struct cmd_context *cmd,
process_all_pvs = dm_list_empty(&arg_pvnames) && dm_list_empty(&arg_tags);
process_all_devices = process_all_pvs && (cmd->command->flags & ENABLE_ALL_DEVS) &&
- arg_count(cmd, all_ARG);
+ (each_flags & EACH_ALL);
/* Needed for a current listing of the global VG namespace. */
if (!only_this_vgname && !lockd_gl(cmd, "sh", 0))
@@ -3498,3 +3499,405 @@ int lvremove_single(struct cmd_context *cmd, struct logical_volume *lv,
return ECMD_PROCESSED;
}
+
+struct pvcreate_device {
+ struct dm_list list;
+ const char *name;
+ struct device *dev;
+};
+
+/*
+ * When a prompt entry is created, save any strings or info
+ * in this struct that are needed for the prompt messages.
+ * The VG/PV structs are not be available when the prompt
+ * is run.
+ */
+struct pvcreate_prompt {
+ struct dm_list list;
+ uint32_t prompt_type;
+ const char *pv_name;
+ const char *vg_name;
+ struct device *dev;
+ unsigned answer : 1;
+ unsigned abort_command : 1;
+};
+
+static void _run_pvcreate_prompt(struct cmd_context *cmd,
+ struct pvcreate_each_params *pep,
+ struct pvcreate_prompt *prompt)
+{
+ if (prompt->type == PROMPT_PVCREATE_PV_IN_VG) {
+ if (pep->force != DONT_PROMPT_OVERRIDE) {
+ pep->answer = 0;
+ log_error("Can't initialize physical volume \"%s\" of volume group \"%s\" without -ff",
+ prompt->pv_name, prompt->vg_name);
+ } else if (pp->yes) {
+ pep->answer = 1;
+ } else {
+ if (yes_no_prompt("Really INITIALIZE physical volume \"%s\" of volume group \"%s\" [y/n]? ",
+ prompt->pv_name, prompt->vg_name) == 'n') {
+ pep->answer = 0;
+ log_error("%s: physical volume not initialized", prompt->pv_name);
+ } else {
+ pep->answer = 1;
+ }
+ }
+ }
+}
+
+
+static pvcreate_device *_pvcreate_list_find_name(struct dm_list *devices, const char *name)
+{
+ struct pvcreate_device *pd;
+
+ dm_list_iterate_items(pd, devices) {
+ if (!strcmp(name, pd->name))
+ return pd;
+ }
+ return NULL;
+}
+
+/*
+ * If this function returns ECMD_FAILED, it will
+ * cause the entire command to fail.
+ *
+ * If this function decides that a arg_names entry cannot be
+ * used, but the command might be able to continue without it,
+ * then it moves that entry from arg_names to arg_fail
+ * and returns ECMD_SUCCESS.
+ *
+ * If this function decides that an arg_names entry could be used
+ * (possibly requiring a prompt), then it moves the entry from
+ * arg_names to arg_create and returns ECMD_SUCCESS.
+ *
+ * Any arg_names entries that are not moved to arg_fail or
+ * arg_create were not found. The caller will decide if the
+ * command can continue if any arg_names entries were not found,
+ * or if any were moved to arg_fail.
+ */
+
+static int _pvcreate_check_single(struct cmd_context *cmd,
+ struct volume_group *vg,
+ struct physical_volume *pv,
+ struct processing_handle *handle)
+{
+ struct pvcreate_each_params *pep = (struct pvcreate_each_params *) handle->custom_handle;
+ struct pvcreate_device *pd;
+ struct pvcreate_prompt *prompt;
+
+ /*
+ * Check if pv uuid matches a uuid specified by the command.
+ */
+ if (pep->uuid_str && id_equal(&pv->id, &pep->id)) {
+ log_error("uuid %s already in use on \"%s\"", pep->uuid_str, pv_dev_name(pv));
+ return ECMD_FAILED;
+ }
+
+ /*
+ * Check if pvcreate has asked for this device.
+ */
+ if (!(pd = _pvcreate_list_find_name(&pep->arg_names, pv_dev_name(pv))))
+ return ECMD_PROCESSED;
+
+#if 0
+ /* TODO: see code in _pvcreate_check() I don't understand */
+ dev = dev_cache_get(pd->name, cmd->full_filter);
+ /* TODO: Do we need to check if dev is NULL here? */
+ /* TODO: obtain_device_list_from_udev, filter_refresh_needed, scan_needed ? */
+#endif
+
+ /*
+ * This test will fail if the device belongs to an MD array.
+ */
+ if (!dev_test_excl(pv->dev)) {
+ /* FIXME Detect whether device-mapper itself is still using it */
+ log_error("Can't open %s exclusively. Mounted filesystem?",
+ pv_dev_name(pv));
+ dm_list_move(&pep->arg_fail, &pd->list);
+ return ECMD_PROCESSED;
+ }
+
+ /*
+ * pvcreate is being run on this device, and it's not a PV,
+ * or is an orphan PV. Neither case requires a prompt.
+ */
+ if (!vg || is_orphan(pv)) {
+ pd->dev = pv->dev;
+ dm_list_move(&pep->arg_create, &pd->list);
+ return ECMD_PROCESSED;
+ }
+
+ /*
+ * pvcreate is being run on this device, but the device is already
+ * a PV in a VG. A prompt or force option is required to use it.
+ */
+
+ if (!(prompt = dm_pool_zalloc(cmd->mem, sizeof(*prompt)))) {
+ log_error("prompt alloc failed");
+ return ECMD_FAILED;
+ }
+
+ prompt->pv_name = dm_pool_strdup(cmd->mem, pd->name);
+ prompt->vg_name = dm_pool_strdup(cmd->mem, vg->name);
+ prompt->prompt_type = PROMPT_PVCREATE_PV_IN_VG;
+ dm_list_add(&pep->prompts, &prompt->list);
+
+ pd->dev = pv->dev;
+ dm_list_move(&pep->arg_create, &pd->list);
+ return ECMD_PROCESSED;
+}
+
+/*
+ * This can be used by pvcreate, vgcreate and vgextend to create PVs.
+ * The callers need to set up the pvcreate_each_params structure based
+ * on command line args. This includes the pv_names field which specifies
+ * the devices to create PVs on.
+ *
+ * This function is a specialized instance of process_each_pv() and
+ * should be called from a high level in the command.
+ *
+ * This function returns ECMD_FAILED if the caller requires all
+ * specified devices to be created, and any of those devices are
+ * not found, or any of them cannot be pvcreated.
+ *
+ * This function returns ECMD_PROCESSED if the caller requires all
+ * specified devices to be created, and all were created, or if
+ * the caller does not require all specified devices to be created
+ * and one or more were created.
+ */
+
+int pvcreate_each_device(struct cmd_context *cmd, struct pvcreate_each_params *pep)
+{
+ struct processing_handle *handle;
+ struct pvcreate_restorable_params rp;
+ struct pvcreate_device *pd, *pd2;
+ struct pvcreate_prompt *prompt, *prompt2;
+ struct physical_volume *pv;
+ const char *pv_name;
+ int must_use_all = (cmd->command->flags & MUST_USE_ALL_ARGS);
+ int ret;
+ int i;
+
+ for (i = 0; i < pep->pv_count; i++) {
+ dm_unescape_colons_and_at_signs(pep->pv_names[i], NULL, NULL);
+
+ if (!(pd = dm_pool_zalloc(cmd->mem, sizeof(*pd)))) {
+ log_error("alloc failed");
+ return ECMD_FAILED;
+ }
+
+ if (!(pd->name = dm_pool_strdup(cmd->mem, pep->pv_names[i]))) {
+ log_error("strdup failed");
+ return ECMD_FAILED;
+ }
+
+ dm_list_add(&pep->arg_names, &pd->list);
+ }
+
+ /*
+ * process all *existing* PVs and devices - this checks if arg_names
+ * entries are known devices, and if so, if they are PVs, and if so,
+ * if they are in a VG.
+ *
+ * If an arg_names entry is found, it's moved to arg_create if it
+ * can be used, or arg_fail if it cannot be used. If it's added
+ * to arg_create but needs a prompt or force option, then an
+ * corresponding prompt entry is added to pep->prompts.
+ *
+ * We don't want this process_each_pv to look at devices that have
+ * been filtered out by this command.
+ * FIXME: does it work that way?
+ */
+
+ if (!(handle = init_processing_handle(cmd))) {
+ log_error("Failed to initialize processing handle.");
+ return ECMD_FAILED;
+ }
+
+ handle->custom_handle = pep;
+
+ cmd->command->flags |= ENABLE_ALL_DEVS;
+
+ ret = process_each_pv(cmd, 0, NULL, NULL, 0, EACH_ALL, handle, _pvcreate_check_single);
+ if (ret == ECMD_FAILED)
+ return ECMD_FAILED;
+
+ destroy_processing_handle(cmd, handle);
+
+ /*
+ * Check if all arg_names were found by process_each_pv.
+ */
+ dm_list_iterate_items(pd, &pep->arg_names)
+ log_error("Device %s not found (or ignored by filtering).", pd->name);
+
+ /*
+ * Can the command continue if some specified devices were not found?
+ */
+ if (!dm_list_empty(&pep->arg_names) && must_use_all)
+ return ECMD_FAILED;
+
+ /*
+ * Can the command continue if some specified devices cannot be used?
+ */
+ if (!dm_list_empty(&pep->arg_fail) && must_use_all)
+ return ECMD_FAILED;
+
+ /*
+ * The command cannot continue if there are no devices to create.
+ */
+ if (dm_list_empty(&pep->arg_create)) {
+ log_error("No devices found.");
+ return ECMD_FAILED;
+ }
+
+ /*
+ * Process any prompts that were gathered during the process_each_pv
+ * checks. Options can override/force/skip prompts.
+ */
+ dm_list_iterate_items_safe(prompt, prompt2, &pep->prompt_devices) {
+ _run_pvcreate_prompt(cmd, pep, prompt);
+
+ if (!prompt->answer) {
+ if ((pd = _pvcreate_list_find_name(&pep->arg_create, prompt->pv_name))) {
+ dm_list_move(&pep->arg_fail, &pd->list);
+ } else {
+ /* should never happen */
+ return_ECMD_FAILED;
+ }
+ }
+
+ if (!dm_list_empty(&pep->arg_fail) && must_use_all)
+ break;
+
+ if (prompt->abort_command)
+ return_ECMD_FAILED;
+
+ if (sigint_caught())
+ return_ECMD_FAILED;
+ }
+
+ if (!dm_list_empty(&pep->arg_fail) && must_use_all)
+ return ECMD_FAILED;
+
+ /*
+ * wipe signatures on devices being created (was in _pvcreate_check)
+ */
+ dm_list_iterate_items_safe(pd, pd2, &pep->arg_create) {
+ if (!wipe_known_signatures(cmd, pd->dev, pd->name, TYPE_LVM1_MEMBER | TYPE_LVM2_MEMBER,
+ 0, pep->yes, pep->force, wiped)) {
+ dm_list_move(&pep->arg_fail, &pd->list);
+ }
+
+ if (!dm_list_empty(&pep->arg_fail) && must_use_all)
+ break;
+
+#if 0
+ /*
+ * FIXME: is this still needed or need changing?
+ * FIXME: replace pd->dev with new dev?
+ *
+ * wipe_known_signatures fires
+ * WATCH event to update udev database. But at the moment,
+ * we have no way to synchronize with such event - we may
+ * end up still seeing the old info in udev db and pvcreate
+ * can fail to proceed because of the device still being
+ * filtered (because of the stale info in udev db).
+ * Disable udev dev-ext source temporarily here for
+ * this reason and rescan with DEV_EXT_NONE dev-ext
+ * source (so filters use DEV_EXT_NONE source).
+ */
+ dev_ext_src = external_device_info_source();
+ if (wiped && (dev_ext_src == DEV_EXT_UDEV))
+ init_external_device_info_source(DEV_EXT_NONE);
+
+ dev = dev_cache_get(pd->name, cmd->full_filter);
+
+ init_external_device_info_source(dev_ext_src);
+
+ if (!dev) {
+ log_error("%s: Couldn't find device. Check your filters?", pd->name);
+ dm_list_move(&pep->arg_fail, &pd->list);
+ }
+#endif
+ }
+
+ if (!dm_list_empty(&pep->arg_fail) && must_use_all)
+ return ECMD_FAILED;
+
+ /*
+ * create pvs on devices
+ */
+ dm_list_iterate_items_safe(pd, pd2, &pep->arg_create) {
+
+ if (!dm_list_empty(&pep->arg_fail) && must_use_all)
+ break;
+
+ pv_name = pd->name;
+
+ /* FIXME: get rid of rp usage in pv_create to avoid this. */
+ rp.restorefile = pep->restorefile;
+ rp.id = pep->id;
+ rp.idp = &pdp->id;
+ rp.ba_start = pep->ba_start;
+ rp.ba_size = pep->ba_size;
+ rp.pe_start = pep->pe_start;
+ rp.extent_count = pep->extent_count;
+ rp.extent_size = pep->extent_size;
+
+ if (!(pv = pv_create(cmd, pd->dev, pep->size, pep->data_alignment,
+ pep->data_alignment_offset, pep->labelsector,
+ pep->pvmetadatacopies, pep->pvmetadatasize,
+ pep->metadataignore, &rp))) {
+ log_error("Failed to setup physical volume \"%s\"", pv_name);
+ dm_list_move(&pep->arg_fail, &pd->list);
+ continue;
+ }
+
+ log_verbose("Set up physical volume for \"%s\" with %" PRIu64
+ " available sectors", pv_name, pv_size(pv));
+
+ pv->status |= UNLABELLED_PV;
+
+ if (!label_remove(pv->dev)) {
+ log_error("Failed to wipe existing label on %s", pv_name);
+ dm_list_move(&pep->arg_fail, &pd->list);
+ continue;
+
+ }
+
+ if (pep->zero) {
+ log_verbose("Zeroing start of device %s", pv_name);
+
+ if (!dev_open_quiet(pv->dev)) {
+ log_error("%s not opened: device not zeroed", pv_name);
+ dm_list_move(&pep->arg_fail, &pd->list);
+ continue;
+ }
+
+ if (!dev_set(pv->dev, UINT64_C(0), (size_t) 2048, 0)) {
+ log_error("%s not wiped: aborting", pv_name);
+ if (!dev_close(dev))
+ stack;
+ dm_list_move(&pep->arg_fail, &pd->list);
+ continue;
+ }
+ if (!dev_close(pv->dev))
+ stack;
+ }
+
+ log_verbose("Writing physical volume data to disk \"%s\"", pv_name);
+
+ if (!pv_write(cmd, pv, 1)) {
+ log_error("Failed to write physical volume \"%s\"", pv_name);
+ dm_list_move(&pep->arg_fail, &pd->list);
+ }
+
+ log_print_unless_silent("Physical volume \"%s\" successfully created", pv_name);
+ }
+
+ if (!dm_list_empty(&pep->arg_fail) && must_use_all)
+ return ECMD_FAILED;
+
+ return ECMD_PROCESSED;
+}
+