diff options
Diffstat (limited to 'tools/vgchange.c')
-rw-r--r-- | tools/vgchange.c | 1234 |
1 files changed, 1029 insertions, 205 deletions
diff --git a/tools/vgchange.c b/tools/vgchange.c index a897a85..e2d3dad 100644 --- a/tools/vgchange.c +++ b/tools/vgchange.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. - * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved. + * Copyright (C) 2004-2013 Red Hat, Inc. All rights reserved. * * This file is part of LVM2. * @@ -10,10 +10,18 @@ * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "tools.h" +#include "lib/device/device_id.h" +#include "lib/label/hints.h" + +struct vgchange_params { + int lock_start_count; + unsigned int lock_start_sanlock : 1; + unsigned int vg_complete_to_activate : 1; +}; /* * Increments *count by the number of _new_ monitored devices. @@ -23,27 +31,26 @@ static int _monitor_lvs_in_vg(struct cmd_context *cmd, { struct lv_list *lvl; struct logical_volume *lv; - struct lvinfo info; int r = 1; dm_list_iterate_items(lvl, &vg->lvs) { lv = lvl->lv; if (!lv_info(cmd, lv, lv_is_thin_pool(lv) ? 1 : 0, - &info, 0, 0) || - !info.exists) + NULL, 0, 0)) continue; /* * FIXME: Need to consider all cases... PVMOVE, etc */ - if (lv->status & PVMOVE) + if (lv_is_pvmove(lv)) continue; if (!monitor_dev_for_events(cmd, lv, 0, reg)) { r = 0; continue; - } else - (*count)++; + } + + (*count)++; } return r; @@ -54,20 +61,13 @@ static int _poll_lvs_in_vg(struct cmd_context *cmd, { struct lv_list *lvl; struct logical_volume *lv; - struct lvinfo info; - int lv_active; int count = 0; dm_list_iterate_items(lvl, &vg->lvs) { lv = lvl->lv; - if (!lv_info(cmd, lv, 0, &info, 0, 0)) - lv_active = 0; - else - lv_active = info.exists; - - if (lv_active && - (lv->status & (PVMOVE|CONVERTING|MERGING))) { + if (lv_is_active(lv) && + (lv_is_pvmove(lv) || lv_is_converting(lv) || lv_is_merging(lv))) { lv_spawn_background_polling(cmd, lv); count++; } @@ -86,7 +86,7 @@ static int _activate_lvs_in_vg(struct cmd_context *cmd, struct volume_group *vg, { struct lv_list *lvl; struct logical_volume *lv; - int count = 0, expected_count = 0; + int count = 0, expected_count = 0, r = 1; sigint_allow(); dm_list_iterate_items(lvl, &vg->lvs) { @@ -95,7 +95,7 @@ static int _activate_lvs_in_vg(struct cmd_context *cmd, struct volume_group *vg, lv = lvl->lv; - if (!lv_is_visible(lv)) + if (!lv_is_visible(lv) && (!cmd->process_component_lvs || !lv_is_component(lv))) continue; /* If LV is sparse, activate origin instead */ @@ -103,83 +103,64 @@ static int _activate_lvs_in_vg(struct cmd_context *cmd, struct volume_group *vg, lv = origin_from_cow(lv); /* Only request activation of snapshot origin devices */ - if ((lv->status & SNAPSHOT) || lv_is_cow(lv)) + if (lv_is_snapshot(lv) || lv_is_cow(lv)) continue; /* Only request activation of mirror LV */ - if ((lv->status & MIRROR_IMAGE) || (lv->status & MIRROR_LOG)) + if (lv_is_mirror_image(lv) || lv_is_mirror_log(lv)) continue; - /* Only request activation of the first replicator-dev LV */ - /* Avoids retry with all heads in case of failure */ - if (lv_is_replicator_dev(lv) && (lv != first_replicator_dev(lv))) + if (lv_is_vdo_pool(lv)) continue; - /* Can't deactivate a pvmove LV */ - /* FIXME There needs to be a controlled way of doing this */ - if (((activate == CHANGE_AN) || (activate == CHANGE_ALN)) && - ((lv->status & PVMOVE) )) + if (lv_activation_skip(lv, activate, arg_is_set(cmd, ignoreactivationskip_ARG))) continue; - /* - * If the LV is active exclusive remotely, - * then ignore it here - */ - if (lv_is_active_exclusive_remotely(lv)) { - log_verbose("%s/%s is exclusively active on" - " a remote node", vg->name, lv->name); + if ((activate == CHANGE_AAY) && + !lv_passes_auto_activation_filter(cmd, lv)) continue; - } - if (activate == CHANGE_AAY && !lv_passes_auto_activation_filter(cmd, lv)) + /* vg NOAUTOACTIVATE flag was already checked */ + if ((activate == CHANGE_AAY) && (lv->status & LV_NOAUTOACTIVATE)) continue; expected_count++; - if (activate == CHANGE_AN) { - if (!deactivate_lv(cmd, lv)) { - stack; - continue; - } - } else if (activate == CHANGE_ALN) { - if (!deactivate_lv_local(cmd, lv)) { - stack; - continue; - } - } else if ((activate == CHANGE_AE) || - lv_is_origin(lv) || - lv_is_thin_type(lv)) { - /* FIXME: duplicated test code with lvchange */ - if (!activate_lv_excl(cmd, lv)) { - stack; - continue; - } - } else if (activate == CHANGE_AAY || activate == CHANGE_ALY) { - if (!activate_lv_local(cmd, lv)) { - stack; - continue; - } - } else if (!activate_lv(cmd, lv)) { + if (!lv_change_activate(cmd, lv, activate)) { stack; + r = 0; continue; } - if (background_polling() && - activate != CHANGE_AN && activate != CHANGE_ALN && - (lv->status & (PVMOVE|CONVERTING|MERGING))) - lv_spawn_background_polling(cmd, lv); - count++; } sigint_restore(); if (expected_count) - log_verbose("%s %d logical volumes in volume group %s", - (activate == CHANGE_AN || activate == CHANGE_ALN)? - "Deactivated" : "Activated", count, vg->name); + log_verbose("%sctivated %d logical volumes in volume group %s.", + is_change_activating(activate) ? "A" : "Dea", + count, vg->name); + + /* + * After sucessfull activation we need to initialise polling + * for all activated LVs in a VG. Possible enhancement would + * be adding --poll y|n cmdline option for pvscan and call + * init_background_polling routine in autoactivation handler. + */ + if (count && is_change_activating(activate) && + !vgchange_background_polling(cmd, vg)) { + stack; + r = 0; + } + + /* Wait until devices are available */ + if (!sync_local_dev_names(vg->cmd)) { + log_error("Failed to sync local devices for VG %s.", vg->name); + r = 0; + } - return (expected_count != count) ? 0 : 1; + return r; } static int _vgchange_monitoring(struct cmd_context *cmd, struct volume_group *vg) @@ -199,12 +180,13 @@ static int _vgchange_monitoring(struct cmd_context *cmd, struct volume_group *vg return r; } -static int _vgchange_background_polling(struct cmd_context *cmd, struct volume_group *vg) +int vgchange_background_polling(struct cmd_context *cmd, struct volume_group *vg) { int polled; - if (lvs_in_vg_activated(vg) && background_polling()) { - polled = _poll_lvs_in_vg(cmd, vg); + if (background_polling()) { + log_debug_activation("Starting background polling for volume group \"%s\".", vg->name); + polled = _poll_lvs_in_vg(cmd, vg); if (polled) log_print_unless_silent("Background polling started for %d logical volume(s) " "in volume group \"%s\"", @@ -215,12 +197,39 @@ static int _vgchange_background_polling(struct cmd_context *cmd, struct volume_g } int vgchange_activate(struct cmd_context *cmd, struct volume_group *vg, - activation_change_t activate) + activation_change_t activate, int vg_complete_to_activate) { - int lv_open, active, monitored = 0, r = 1, do_activate = 1; + int lv_open, active, monitored = 0, r = 1; + const struct lv_list *lvl; + struct pv_list *pvl; + int do_activate = is_change_activating(activate); - if ((activate == CHANGE_AN) || (activate == CHANGE_ALN)) - do_activate = 0; + /* + * We can get here in the odd case where an LV is already active in + * a foreign VG, which allows the VG to be accessed by vgchange -a + * so the LV can be deactivated. + */ + if (vg->system_id && vg->system_id[0] && + cmd->system_id && cmd->system_id[0] && + strcmp(vg->system_id, cmd->system_id) && + do_activate) { + log_error("Cannot activate LVs in a foreign VG."); + return 0; + } + + if ((activate == CHANGE_AAY) && (vg->status & NOAUTOACTIVATE)) { + log_debug("Autoactivation is disabled for VG %s.", vg->name); + return 1; + } + + if (do_activate && vg_complete_to_activate) { + dm_list_iterate_items(pvl, &vg->pvs) { + if (!pvl->pv->dev) { + log_print("VG %s is incomplete.", vg->name); + return 1; + } + } + } /* * Safe, since we never write out new metadata here. Required for @@ -229,15 +238,28 @@ int vgchange_activate(struct cmd_context *cmd, struct volume_group *vg, cmd->handles_missing_pvs = 1; /* FIXME: Force argument to deactivate them? */ - if (!do_activate && (lv_open = lvs_in_vg_opened(vg))) { - log_error("Can't deactivate volume group \"%s\" with %d open " - "logical volume(s)", vg->name, lv_open); - return 0; + if (!do_activate) { + dm_list_iterate_items(lvl, &vg->lvs) + label_scan_invalidate_lv(cmd, lvl->lv); + + if ((lv_open = lvs_in_vg_opened(vg))) { + dm_list_iterate_items(lvl, &vg->lvs) { + if (lv_is_visible(lvl->lv) && + !lv_is_vdo_pool(lvl->lv) && // FIXME: API skip flag missing + !lv_check_not_in_use(lvl->lv, 1)) { + log_error("Can't deactivate volume group \"%s\" with %d open logical volume(s)", + vg->name, lv_open); + return 0; + } + } + } } /* FIXME Move into library where clvmd can use it */ if (do_activate) check_current_backup(vg); + else /* Component LVs might be active, support easy deactivation */ + cmd->process_component_lvs = 1; if (do_activate && (active = lvs_in_vg_activated(vg))) { log_verbose("%d logical volume(s) in volume group \"%s\" " @@ -252,13 +274,14 @@ int vgchange_activate(struct cmd_context *cmd, struct volume_group *vg, } } - if (!_activate_lvs_in_vg(cmd, vg, activate)) + if (!_activate_lvs_in_vg(cmd, vg, activate)) { + stack; r = 0; + } /* Print message only if there was not found a missing VG */ - if (!vg->cmd_missing_vgs) - log_print_unless_silent("%d logical volume(s) in volume group \"%s\" now active", - lvs_in_vg_activated(vg), vg->name); + log_print_unless_silent("%d logical volume(s) in volume group \"%s\" now active", + lvs_in_vg_activated(vg), vg->name); return r; } @@ -266,10 +289,8 @@ static int _vgchange_refresh(struct cmd_context *cmd, struct volume_group *vg) { log_verbose("Refreshing volume group \"%s\"", vg->name); - if (!vg_refresh_visible(cmd, vg)) { - stack; - return 0; - } + if (!vg_refresh_visible(cmd, vg)) + return_0; return 1; } @@ -296,7 +317,7 @@ static int _vgchange_alloc(struct cmd_context *cmd, struct volume_group *vg) static int _vgchange_resizeable(struct cmd_context *cmd, struct volume_group *vg) { - int resizeable = !strcmp(arg_str_value(cmd, resizeable_ARG, "n"), "y"); + int resizeable = arg_int_value(cmd, resizeable_ARG, 0); if (resizeable && vg_is_resizeable(vg)) { log_error("Volume group \"%s\" is already resizeable", @@ -318,25 +339,22 @@ static int _vgchange_resizeable(struct cmd_context *cmd, return 1; } -static int _vgchange_clustered(struct cmd_context *cmd, - struct volume_group *vg) +static int _vgchange_autoactivation(struct cmd_context *cmd, + struct volume_group *vg) { - int clustered = !strcmp(arg_str_value(cmd, clustered_ARG, "n"), "y"); - - if (clustered && (vg_is_clustered(vg))) { - log_error("Volume group \"%s\" is already clustered", - vg->name); - return 0; - } + int aa_no_arg = !arg_int_value(cmd, setautoactivation_ARG, 0); + int aa_no_meta = (vg->status & NOAUTOACTIVATE) ? 1 : 0; - if (!clustered && !(vg_is_clustered(vg))) { - log_error("Volume group \"%s\" is already not clustered", - vg->name); + if ((aa_no_arg && aa_no_meta) || (!aa_no_arg && !aa_no_meta)) { + log_error("Volume group autoactivation is already %s.", + aa_no_arg ? "no" : "yes"); return 0; } - if (!vg_set_clustered(vg, clustered)) - return_0; + if (aa_no_arg) + vg->status |= NOAUTOACTIVATE; + else + vg->status &= ~NOAUTOACTIVATE; return 1; } @@ -368,22 +386,28 @@ static int _vgchange_pesize(struct cmd_context *cmd, struct volume_group *vg) uint32_t extent_size; if (arg_uint64_value(cmd, physicalextentsize_ARG, 0) > MAX_EXTENT_SIZE) { - log_error("Physical extent size cannot be larger than %s", - display_size(cmd, (uint64_t) MAX_EXTENT_SIZE)); + log_warn("WARNING: Physical extent size cannot be larger than %s.", + display_size(cmd, (uint64_t) MAX_EXTENT_SIZE)); return 1; } extent_size = arg_uint_value(cmd, physicalextentsize_ARG, 0); /* FIXME: remove check - redundant with vg_change_pesize */ if (extent_size == vg->extent_size) { - log_error("Physical extent size of VG %s is already %s", - vg->name, display_size(cmd, (uint64_t) extent_size)); + log_warn("WARNING: Physical extent size of VG %s is already %s.", + vg->name, display_size(cmd, (uint64_t) extent_size)); return 1; } if (!vg_set_extent_size(vg, extent_size)) return_0; + if (!vg_check_pv_dev_block_sizes(vg)) { + log_error("Failed to change physical extent size for VG %s.", + vg->name); + return 0; + } + return 1; } @@ -401,12 +425,15 @@ static int _vgchange_uuid(struct cmd_context *cmd __attribute__((unused)), struct volume_group *vg) { struct lv_list *lvl; + struct id old_vg_id; if (lvs_in_vg_activated(vg)) { log_error("Volume group has active logical volumes"); return 0; } + memcpy(&old_vg_id, &vg->id, ID_LEN); + if (!id_create(&vg->id)) { log_error("Failed to generate new random UUID for VG %s.", vg->name); @@ -417,6 +444,12 @@ static int _vgchange_uuid(struct cmd_context *cmd __attribute__((unused)), memcpy(&lvl->lv->lvid, &vg->id, sizeof(vg->id)); } + /* + * If any LVs in this VG have PVs stacked on them, then + * update the device_id of the stacked PV. + */ + device_id_update_vg_uuid(cmd, vg, &old_vg_id); + return 1; } @@ -425,13 +458,16 @@ static int _vgchange_metadata_copies(struct cmd_context *cmd, { uint32_t mda_copies = arg_uint_value(cmd, vgmetadatacopies_ARG, DEFAULT_VGMETADATACOPIES); + log_debug("vgchange_metadata_copies new %u vg_mda_copies %u D %u", + mda_copies, vg_mda_copies(vg), DEFAULT_VGMETADATACOPIES); + if (mda_copies == vg_mda_copies(vg)) { if (vg_mda_copies(vg) == VGMETADATACOPIES_UNMANAGED) - log_error("Number of metadata copies for VG %s is already unmanaged.", - vg->name); + log_warn("WARNING: Number of metadata copies for VG %s is already unmanaged.", + vg->name); else - log_error("Number of metadata copies for VG %s is already %" PRIu32, - vg->name, mda_copies); + log_warn("WARNING: Number of metadata copies for VG %s is already %u.", + vg->name, mda_copies); return 1; } @@ -441,35 +477,213 @@ static int _vgchange_metadata_copies(struct cmd_context *cmd, return 1; } -static int vgchange_single(struct cmd_context *cmd, const char *vg_name, - struct volume_group *vg, - void *handle __attribute__((unused))) +static int _vgchange_profile(struct cmd_context *cmd, + struct volume_group *vg) +{ + const char *old_profile_name, *new_profile_name; + struct profile *new_profile; + + old_profile_name = vg->profile ? vg->profile->name : "(no profile)"; + + if (arg_is_set(cmd, detachprofile_ARG)) { + new_profile_name = "(no profile)"; + vg->profile = NULL; + } else { + if (arg_is_set(cmd, metadataprofile_ARG)) + new_profile_name = arg_str_value(cmd, metadataprofile_ARG, NULL); + else + new_profile_name = arg_str_value(cmd, profile_ARG, NULL); + if (!(new_profile = add_profile(cmd, new_profile_name, CONFIG_PROFILE_METADATA))) + return_0; + vg->profile = new_profile; + } + + log_verbose("Changing configuration profile for VG %s: %s -> %s.", + vg->name, old_profile_name, new_profile_name); + + return 1; +} + +/* + * This function will not be called unless the local host is allowed to use the + * VG. Either the VG has no system_id, or the VG and host have matching + * system_ids, or the host has the VG's current system_id in its + * extra_system_ids list. This function is not allowed to change the system_id + * of a foreign VG (VG owned by another host). + */ +static int _vgchange_system_id(struct cmd_context *cmd, struct volume_group *vg) +{ + const char *system_id; + const char *system_id_arg_str = arg_str_value(cmd, systemid_ARG, NULL); + + if (!(system_id = system_id_from_string(cmd, system_id_arg_str))) { + log_error("Unable to set system ID."); + return 0; + } + + if (!strcmp(vg->system_id, system_id)) { + log_error("Volume Group system ID is already \"%s\".", vg->system_id); + return 0; + } + + if (!*system_id && cmd->system_id && strcmp(system_id, cmd->system_id)) { + log_warn("WARNING: Removing the system ID allows unsafe access from other hosts."); + + if (!arg_is_set(cmd, yes_ARG) && + yes_no_prompt("Remove system ID %s from volume group %s? [y/n]: ", + vg->system_id, vg->name) == 'n') { + log_error("System ID of volume group %s not changed.", vg->name); + return 0; + } + } + + if (*system_id && (!cmd->system_id || strcmp(system_id, cmd->system_id))) { + if (lvs_in_vg_activated(vg)) { + log_error("Logical Volumes in VG %s must be deactivated before system ID can be changed.", + vg->name); + return 0; + } + + if (cmd->system_id) + log_warn("WARNING: Requested system ID %s does not match local system ID %s.", + system_id, cmd->system_id ? : ""); + else + log_warn("WARNING: No local system ID is set."); + log_warn("WARNING: Volume group %s might become inaccessible from this machine.", + vg->name); + + if (!arg_is_set(cmd, yes_ARG) && + yes_no_prompt("Set foreign system ID %s on volume group %s? [y/n]: ", + system_id, vg->name) == 'n') { + log_error("Volume group %s system ID not changed.", vg->name); + return 0; + } + } + + log_verbose("Changing system ID for VG %s from \"%s\" to \"%s\".", + vg->name, vg->system_id, system_id); + + vg->system_id = system_id; + + return 1; +} + +static int _passes_lock_start_filter(struct cmd_context *cmd, + struct volume_group *vg, + const int cfg_id) { - int archived = 0; - int i; + const struct dm_config_node *cn; + const struct dm_config_value *cv; + const char *str; + + /* undefined list means no restrictions, all vg names pass */ + + cn = find_config_tree_array(cmd, cfg_id, NULL); + if (!cn) + return 1; - static struct { + /* with a defined list, the vg name must be included to pass */ + + for (cv = cn->v; cv; cv = cv->next) { + if (cv->type == DM_CFG_EMPTY_ARRAY) + break; + if (cv->type != DM_CFG_STRING) { + log_error("Ignoring invalid string in lock_start list"); + continue; + } + str = cv->v.str; + if (!*str) { + log_error("Ignoring empty string in config file"); + continue; + } + + /* ignoring tags for now */ + + if (!strcmp(str, vg->name)) + return 1; + } + + return 0; +} + +static int _vgchange_lock_start(struct cmd_context *cmd, struct volume_group *vg, + struct vgchange_params *vp) +{ + const char *start_opt = arg_str_value(cmd, lockopt_ARG, NULL); + int auto_opt = 0; + int exists = 0; + int r; + + if (!vg_is_shared(vg)) + return 1; + + if (arg_is_set(cmd, force_ARG)) + goto do_start; + + /* + * Recognize both "auto" and "autonowait" options. + * Any waiting is done at the end of vgchange. + */ + if (start_opt && !strncmp(start_opt, "auto", 4)) + auto_opt = 1; + + if (!_passes_lock_start_filter(cmd, vg, activation_lock_start_list_CFG)) { + log_verbose("Not starting %s since it does not pass lock_start_list", vg->name); + return 1; + } + + if (auto_opt && !_passes_lock_start_filter(cmd, vg, activation_auto_lock_start_list_CFG)) { + log_verbose("Not starting %s since it does not pass auto_lock_start_list", vg->name); + return 1; + } + +do_start: + r = lockd_start_vg(cmd, vg, 0, &exists); + + if (r) + vp->lock_start_count++; + else if (exists) + vp->lock_start_count++; + if (!strcmp(vg->lock_type, "sanlock")) + vp->lock_start_sanlock = 1; + + return r; +} + +static int _vgchange_lock_stop(struct cmd_context *cmd, struct volume_group *vg) +{ + return lockd_stop_vg(cmd, vg); +} + +static int _vgchange_single(struct cmd_context *cmd, const char *vg_name, + struct volume_group *vg, + struct processing_handle *handle) +{ + struct vgchange_params *vp = (struct vgchange_params *)handle->custom_handle; + int ret = ECMD_PROCESSED; + unsigned i; + activation_change_t activate; + int changed = 0; + + static const struct { int arg; int (*fn)(struct cmd_context *cmd, struct volume_group *vg); } _vgchange_args[] = { { logicalvolume_ARG, &_vgchange_logicalvolume }, { maxphysicalvolumes_ARG, &_vgchange_physicalvolumes }, { resizeable_ARG, &_vgchange_resizeable }, + { setautoactivation_ARG, &_vgchange_autoactivation }, { deltag_ARG, &_vgchange_deltag }, { addtag_ARG, &_vgchange_addtag }, { physicalextentsize_ARG, &_vgchange_pesize }, { uuid_ARG, &_vgchange_uuid }, { alloc_ARG, &_vgchange_alloc }, - { clustered_ARG, &_vgchange_clustered }, { vgmetadatacopies_ARG, &_vgchange_metadata_copies }, - { -1, NULL }, + { metadataprofile_ARG, &_vgchange_profile }, + { profile_ARG, &_vgchange_profile }, + { detachprofile_ARG, &_vgchange_profile }, }; - if (vg_is_exported(vg)) { - log_error("Volume group \"%s\" is exported", vg_name); - return ECMD_FAILED; - } - /* * FIXME: DEFAULT_BACKGROUND_POLLING should be "unspecified". * If --poll is explicitly provided use it; otherwise polling @@ -479,145 +693,755 @@ static int vgchange_single(struct cmd_context *cmd, const char *vg_name, * * Do not initiate any polling if --sysinit option is used. */ - init_background_polling(arg_count(cmd, sysinit_ARG) ? 0 : + init_background_polling(arg_is_set(cmd, sysinit_ARG) ? 0 : arg_int_value(cmd, poll_ARG, DEFAULT_BACKGROUND_POLLING)); - for (i = 0; _vgchange_args[i].arg >= 0; i++) { - if (arg_count(cmd, _vgchange_args[i].arg)) { - if (!archived && !archive(vg)) { - stack; - return ECMD_FAILED; - } - archived = 1; - if (!_vgchange_args[i].fn(cmd, vg)) { - stack; - return ECMD_FAILED; - } + for (i = 0; i < DM_ARRAY_SIZE(_vgchange_args); ++i) { + if (arg_is_set(cmd, _vgchange_args[i].arg)) { + if (!_vgchange_args[i].fn(cmd, vg)) + return_ECMD_FAILED; + changed = 1; } } - if (archived) { - if (!vg_write(vg) || !vg_commit(vg)) { - stack; - return ECMD_FAILED; + if (changed) { + if (!vg_write(vg) || !vg_commit(vg)) + return_ECMD_FAILED; + + log_print_unless_silent("Volume group \"%s\" successfully changed", 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)) + return_ECMD_FAILED; + } else if (arg_is_set(cmd, refresh_ARG)) { + /* refreshes the visible LVs (which starts polling) */ + if (!_vgchange_refresh(cmd, vg)) + return_ECMD_FAILED; + } else { + /* -ay* will have already done monitoring changes */ + if (arg_is_set(cmd, monitor_ARG) && + !_vgchange_monitoring(cmd, vg)) + return_ECMD_FAILED; + + /* When explicitelly specified --poll */ + if (arg_is_set(cmd, poll_ARG) && + !vgchange_background_polling(cmd, vg)) + return_ECMD_FAILED; + } + + return ret; +} + +static int _vgchange_autoactivation_setup(struct cmd_context *cmd, + struct vgchange_params *vp, + int *skip_command, + const char **vgname_ret, + uint32_t *flags) +{ + const char *aa; + char *vgname = NULL; + int vg_locked = 0; + int found_none = 0, found_all = 0, found_incomplete = 0; + + if (!(aa = arg_str_value(cmd, autoactivation_ARG, NULL))) + return_0; + + if (strcmp(aa, "event")) { + log_print("Skip vgchange for unknown autoactivation value."); + *skip_command = 1; + return 1; + } + + if (!find_config_tree_bool(cmd, global_event_activation_CFG, NULL)) { + log_print("Skip vgchange for event and event_activation=0."); + *skip_command = 1; + return 1; + } + + vp->vg_complete_to_activate = 1; + cmd->use_hints = 0; + + /* + * Add an option to skip the pvs_online optimization? e.g. + * "online_skip" in --autoactivation / auto_activation_settings + * + * if (online_skip) + * return 1; + */ + + /* reads devices file, does not populate dev-cache */ + if (!setup_devices_for_online_autoactivation(cmd)) + return_0; + + get_single_vgname_cmd_arg(cmd, NULL, &vgname); + + /* + * 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 + * can_use_one_scan avoids the normal rescan.) If this early lock_vol + * fails, continue and use the normal lock_vol in _vg_read. + */ + if (vgname) { + if (!lock_vol(cmd, vgname, LCK_VG_WRITE, NULL)) { + log_debug("Failed early VG locking for autoactivation."); + } else { + *flags |= READ_WITHOUT_LOCK; + cmd->can_use_one_scan = 1; + vg_locked = 1; } + } - backup(vg); + /* + * Perform label_scan on PVs that are online (per /run/lvm files) + * for the given VG (or when no VG name is given, all online PVs.) + * If this fails, the caller will do a normal process_each_vg without + * optimizations (which will do a full label_scan.) + */ + if (!label_scan_vg_online(cmd, vgname, &found_none, &found_all, &found_incomplete)) { + log_print("PVs online error%s%s, using all devices.", vgname ? " for VG " : "", vgname ?: ""); + goto bad; + } - log_print_unless_silent("Volume group \"%s\" successfully changed", vg->name); + /* + * Not the expected usage, activate any VGs that are complete based on + * pvs_online. Only online pvs are used. + */ + if (!vgname) { + *flags |= PROCESS_SKIP_SCAN; + return 1; } - if (arg_count(cmd, activate_ARG)) { - if (!vgchange_activate(cmd, vg, (activation_change_t) - arg_uint_value(cmd, activate_ARG, CHANGE_AY))) - return ECMD_FAILED; + /* + * The expected and optimal usage, which is the purpose of + * this function. We expect online files to be found for + * all PVs because the udev rule calls + * vgchange -aay --autoactivation event <vgname> + * only after all PVs for vgname are found online. + */ + if (found_all) { + *flags |= PROCESS_SKIP_SCAN; + *vgname_ret = vgname; + return 1; } - if (arg_count(cmd, refresh_ARG)) { - /* refreshes the visible LVs (which starts polling) */ - if (!_vgchange_refresh(cmd, vg)) - return ECMD_FAILED; + /* + * Not expected usage, no online pvs for the vgname were found. The + * caller will fall back to process_each doing a full label_scan to + * look for the VG. (No optimization used.) + */ + if (found_none) { + log_print("PVs online not found for VG %s, using all devices.", vgname); + goto bad; } - if (!arg_count(cmd, activate_ARG) && - !arg_count(cmd, refresh_ARG) && - arg_count(cmd, monitor_ARG)) { - /* -ay* will have already done monitoring changes */ - if (!_vgchange_monitoring(cmd, vg)) - return ECMD_FAILED; + /* + * Not expected usage, only some online pvs for the vgname were found. + * The caller will fall back to process_each doing a full label_scan to + * look for all PVs in the VG. (No optimization used.) + */ + if (found_incomplete) { + log_print("PVs online incomplete for VG %s, using all devicess.", vgname); + goto bad; } - if (!arg_count(cmd, refresh_ARG) && - background_polling()) - if (!_vgchange_background_polling(cmd, vg)) - return ECMD_FAILED; + /* + * Shouldn't happen, the caller will fall back to standard + * process_each. (No optimization used.) + */ + log_print("PVs online unknown for VG %s, using all devices.", vgname); + + bad: + /* + * The online scanning optimization didn't work, so undo the vg + * locking optimization before falling back to normal processing. + */ + if (vg_locked) { + unlock_vg(cmd, NULL, vgname); + *flags &= ~READ_WITHOUT_LOCK; + cmd->can_use_one_scan = 0; + } + + free(vgname); + + return 1; - return ECMD_PROCESSED; } int vgchange(struct cmd_context *cmd, int argc, char **argv) { - /* Update commands that can be combined */ + struct vgchange_params vp = { 0 }; + struct processing_handle *handle; + const char *vgname = NULL; + uint32_t flags = 0; + int ret; + + int noupdate = + arg_is_set(cmd, activate_ARG) || + arg_is_set(cmd, monitor_ARG) || + arg_is_set(cmd, poll_ARG) || + arg_is_set(cmd, refresh_ARG); + int update_partial_safe = - arg_count(cmd, deltag_ARG) || - arg_count(cmd, addtag_ARG); + arg_is_set(cmd, deltag_ARG) || + arg_is_set(cmd, addtag_ARG) || + arg_is_set(cmd, metadataprofile_ARG) || + arg_is_set(cmd, profile_ARG) || + arg_is_set(cmd, detachprofile_ARG); + int update_partial_unsafe = - arg_count(cmd, logicalvolume_ARG) || - arg_count(cmd, maxphysicalvolumes_ARG) || - arg_count(cmd, resizeable_ARG) || - arg_count(cmd, uuid_ARG) || - arg_count(cmd, physicalextentsize_ARG) || - arg_count(cmd, clustered_ARG) || - arg_count(cmd, alloc_ARG) || - arg_count(cmd, vgmetadatacopies_ARG); + arg_is_set(cmd, logicalvolume_ARG) || + arg_is_set(cmd, maxphysicalvolumes_ARG) || + arg_is_set(cmd, resizeable_ARG) || + arg_is_set(cmd, setautoactivation_ARG) || + arg_is_set(cmd, uuid_ARG) || + arg_is_set(cmd, physicalextentsize_ARG) || + arg_is_set(cmd, alloc_ARG) || + arg_is_set(cmd, vgmetadatacopies_ARG); + int update = update_partial_safe || update_partial_unsafe; - if (!update && - !arg_count(cmd, activate_ARG) && - !arg_count(cmd, monitor_ARG) && - !arg_count(cmd, poll_ARG) && - !arg_count(cmd, refresh_ARG)) { - log_error("Need 1 or more of -a, -c, -l, -p, -s, -x, " - "--refresh, --uuid, --alloc, --addtag, --deltag, " - "--monitor, --poll, --vgmetadatacopies or " - "--metadatacopies"); + if (!update && !noupdate) { + log_error("Need one or more command options."); + return EINVALID_CMD_LINE; + } + + if ((arg_is_set(cmd, profile_ARG) || arg_is_set(cmd, metadataprofile_ARG)) && + arg_is_set(cmd, detachprofile_ARG)) { + log_error("Only one of --metadataprofile and --detachprofile permitted."); return EINVALID_CMD_LINE; } - if (arg_count(cmd, activate_ARG) && arg_count(cmd, refresh_ARG)) { + if (arg_is_set(cmd, activate_ARG) && arg_is_set(cmd, refresh_ARG)) { log_error("Only one of -a and --refresh permitted."); return EINVALID_CMD_LINE; } - if ((arg_count(cmd, ignorelockingfailure_ARG) || - arg_count(cmd, sysinit_ARG)) && update) { + if ((arg_is_set(cmd, ignorelockingfailure_ARG) || + arg_is_set(cmd, sysinit_ARG)) && update) { log_error("Only -a permitted with --ignorelockingfailure and --sysinit"); return EINVALID_CMD_LINE; } - if (arg_count(cmd, activate_ARG) && - (arg_count(cmd, monitor_ARG) || arg_count(cmd, poll_ARG))) { - int activate = arg_uint_value(cmd, activate_ARG, 0); - if (activate == CHANGE_AN || activate == CHANGE_ALN) { + if (arg_is_set(cmd, activate_ARG) && + (arg_is_set(cmd, monitor_ARG) || arg_is_set(cmd, poll_ARG))) { + if (!is_change_activating((activation_change_t) arg_uint_value(cmd, activate_ARG, 0))) { log_error("Only -ay* allowed with --monitor or --poll."); return EINVALID_CMD_LINE; } } - if (arg_count(cmd, poll_ARG) && arg_count(cmd, sysinit_ARG)) { + if (arg_is_set(cmd, poll_ARG) && arg_is_set(cmd, sysinit_ARG)) { log_error("Only one of --poll and --sysinit permitted."); return EINVALID_CMD_LINE; } - if (arg_count(cmd, activate_ARG) == 1 - && arg_count(cmd, autobackup_ARG)) { - log_error("-A option not necessary with -a option"); - return EINVALID_CMD_LINE; - } - - if (arg_count(cmd, maxphysicalvolumes_ARG) && + if (arg_is_set(cmd, maxphysicalvolumes_ARG) && arg_sign_value(cmd, maxphysicalvolumes_ARG, SIGN_NONE) == SIGN_MINUS) { log_error("MaxPhysicalVolumes may not be negative"); return EINVALID_CMD_LINE; } - if (arg_count(cmd, physicalextentsize_ARG) && + if (arg_is_set(cmd, physicalextentsize_ARG) && arg_sign_value(cmd, physicalextentsize_ARG, SIGN_NONE) == SIGN_MINUS) { log_error("Physical extent size may not be negative"); return EINVALID_CMD_LINE; } - if (arg_count(cmd, sysinit_ARG) && lvmetad_active() && - arg_uint_value(cmd, activate_ARG, 0) == CHANGE_AAY) { - log_warn("lvmetad is active while using --sysinit -a ay, " - "skipping manual activation"); - return ECMD_PROCESSED; + if (arg_is_set(cmd, clustered_ARG) && !argc && !arg_is_set(cmd, yes_ARG) && + (yes_no_prompt("Change clustered property of all volumes groups? [y/n]: ") == 'n')) { + log_error("No volume groups changed."); + return ECMD_FAILED; } if (!update || !update_partial_unsafe) cmd->handles_missing_pvs = 1; - return process_each_vg(cmd, argc, argv, update ? READ_FOR_UPDATE : 0, - NULL, &vgchange_single); + if (noupdate) + cmd->ignore_device_name_mismatch = 1; + + /* + * If the devices file includes PVs stacked on LVs, then + * vgchange --uuid may need to update the devices file. + * No PV-on-LV stacked is done without scan_lvs set. + */ + if (arg_is_set(cmd, uuid_ARG) && cmd->scan_lvs) + cmd->edit_devices_file = 1; + + /* + * Include foreign VGs that contain active LVs. + * That shouldn't happen in general, but if it does by some + * mistake, then we want to allow those LVs to be deactivated. + */ + if (arg_is_set(cmd, activate_ARG)) + cmd->include_active_foreign_vgs = 1; + + /* The default vg lock mode is ex, but these options only need sh. */ + if ((cmd->command->command_enum == vgchange_activate_CMD) || + (cmd->command->command_enum == vgchange_refresh_CMD)) { + cmd->lockd_vg_default_sh = 1; + /* Allow deactivating if locks fail. */ + if (is_change_activating((activation_change_t)arg_uint_value(cmd, activate_ARG, CHANGE_AY))) + cmd->lockd_vg_enforce_sh = 1; + } + + if (arg_is_set(cmd, autoactivation_ARG)) { + int skip_command = 0; + if (!_vgchange_autoactivation_setup(cmd, &vp, &skip_command, &vgname, &flags)) + return ECMD_FAILED; + if (skip_command) + return ECMD_PROCESSED; + } + + /* + * Do not use udev for device listing or device info because + * vgchange --monitor y is called during boot when udev is being + * initialized and is not yet ready to be used. + */ + if (arg_is_set(cmd, monitor_ARG) && + arg_int_value(cmd, monitor_ARG, DEFAULT_DMEVENTD_MONITOR)) { + init_obtain_device_list_from_udev(0); + init_external_device_info_source(DEV_EXT_NONE); + } + + if (update) + flags |= READ_FOR_UPDATE; + else if (arg_is_set(cmd, activate_ARG)) + flags |= READ_FOR_ACTIVATE; + + if (!(handle = init_processing_handle(cmd, NULL))) { + log_error("Failed to initialize processing handle."); + return ECMD_FAILED; + } + + handle->custom_handle = &vp; + + ret = process_each_vg(cmd, argc, argv, vgname, NULL, flags, 0, handle, &_vgchange_single); + + destroy_processing_handle(cmd, handle); + return ret; +} + +static int _vgchange_locktype(struct cmd_context *cmd, struct volume_group *vg) +{ + const char *lock_type = arg_str_value(cmd, locktype_ARG, NULL); + const char *lockopt = arg_str_value(cmd, lockopt_ARG, NULL); + struct lv_list *lvl; + struct logical_volume *lv; + int lv_lock_count = 0; + + /* Special recovery case. */ + if (lock_type && lockopt && !strcmp(lock_type, "none") && !strcmp(lockopt, "force")) { + vg->status &= ~CLUSTERED; + vg->lock_type = "none"; + vg->lock_args = NULL; + + dm_list_iterate_items(lvl, &vg->lvs) + lvl->lv->lock_args = NULL; + + return 1; + } + + if (!vg->lock_type) { + if (vg_is_clustered(vg)) + vg->lock_type = "clvm"; + else + vg->lock_type = "none"; + } + + if (lock_type && !strcmp(vg->lock_type, lock_type)) { + log_warn("WARNING: New lock type %s matches the current lock type %s.", + lock_type, vg->lock_type); + return 1; + } + + if (is_lockd_type(vg->lock_type) && is_lockd_type(lock_type)) { + log_error("Cannot change lock type directly from \"%s\" to \"%s\".", + vg->lock_type, lock_type); + log_error("First change lock type to \"none\", then to \"%s\".", + lock_type); + return 0; + } + + /* + * When lvm is currently using lvmlockd, this function can: + * - change none to lockd type + * - change none to clvm (with warning about not being able to use it) + * - change lockd type to none + * - change lockd type to clvm (with warning about not being able to use it) + * - change clvm to none + * - change clvm to lockd type + */ + + if (lvs_in_vg_activated(vg)) { + log_error("Changing VG %s lock type not allowed with active LVs", + vg->name); + return 0; + } + + /* clvm to none */ + if (lock_type && !strcmp(vg->lock_type, "clvm") && !strcmp(lock_type, "none")) { + vg->status &= ~CLUSTERED; + vg->lock_type = "none"; + return 1; + } + + /* clvm to ..., first undo clvm */ + if (!strcmp(vg->lock_type, "clvm")) { + vg->status &= ~CLUSTERED; + } + + /* + * lockd type to ..., first undo lockd type + */ + if (is_lockd_type(vg->lock_type)) { + if (!lockd_free_vg_before(cmd, vg, 1)) + return 0; + + lockd_free_vg_final(cmd, vg); + + vg->status &= ~CLUSTERED; + vg->lock_type = "none"; + vg->lock_args = NULL; + + dm_list_iterate_items(lvl, &vg->lvs) + lvl->lv->lock_args = NULL; + } + + /* ... to lockd type */ + if (is_lockd_type(lock_type)) { + /* + * For lock_type dlm, lockd_init_vg() will do a single + * vg_write() that sets lock_type, sets lock_args, clears + * system_id, and sets all LV lock_args to dlm. + * For lock_type sanlock, lockd_init_vg() needs to know + * how many LV locks are needed so that it can make the + * sanlock lv large enough. + */ + dm_list_iterate_items(lvl, &vg->lvs) { + lv = lvl->lv; + + if (lockd_lv_uses_lock(lv)) { + lv_lock_count++; + + if (!strcmp(lock_type, "dlm")) + lv->lock_args = "dlm"; + } + } + + /* + * See below. We cannot set valid LV lock_args until stage 1 + * of the change is done, so we need to skip the validation of + * the lock_args during stage 1. + */ + if (!strcmp(lock_type, "sanlock")) + vg->skip_validate_lock_args = 1; + + vg->system_id = NULL; + + if (!lockd_init_vg(cmd, vg, lock_type, lv_lock_count)) { + log_error("Failed to initialize lock args for lock type %s", lock_type); + return 0; + } + + /* + * For lock_type sanlock, there must be multiple steps + * because the VG needs an active lvmlock LV before + * LV lock areas can be allocated, which must be done + * before LV lock_args are written. So, the LV lock_args + * remain unset during the first stage of the conversion. + * + * Stage 1: + * lockd_init_vg() creates and activates the lvmlock LV, + * then sets lock_type, sets lock_args, and clears system_id. + * + * Stage 2: + * We get here, and can now set LV lock_args. This uses + * the standard code path for allocating LV locks in + * vg_write() by setting LV lock_args to "pending", + * which tells vg_write() to call lockd_init_lv() + * and sets the lv->lock_args value before writing the VG. + */ + if (!strcmp(lock_type, "sanlock")) { + dm_list_iterate_items(lvl, &vg->lvs) { + lv = lvl->lv; + if (lockd_lv_uses_lock(lv)) + lv->lock_args = "pending"; + } + + vg->skip_validate_lock_args = 0; + } + + return 1; + } + + /* ... to none */ + if (lock_type && !strcmp(lock_type, "none")) { + vg->lock_type = NULL; + vg->system_id = cmd->system_id ? dm_pool_strdup(vg->vgmem, cmd->system_id) : NULL; + return 1; + } + + log_error("Cannot change to unknown lock type %s", lock_type); + return 0; +} + +static int _vgchange_locktype_single(struct cmd_context *cmd, const char *vg_name, + struct volume_group *vg, + struct processing_handle *handle) +{ + if (!_vgchange_locktype(cmd, vg)) + return_ECMD_FAILED; + + if (!vg_write(vg) || !vg_commit(vg)) + return_ECMD_FAILED; + + /* + * When init_vg_sanlock is called for vgcreate, the lockspace remains + * started and lvmlock remains active, but when called for + * vgchange --locktype sanlock, the lockspace is not started so the + * lvmlock LV should be deactivated at the end. vg_write writes the + * new leases to lvmlock, so we need to wait until after vg_write to + * deactivate it. + */ + if (vg->lock_type && !strcmp(vg->lock_type, "sanlock") && + (cmd->command->command_enum == vgchange_locktype_CMD)) { + if (!deactivate_lv(cmd, vg->sanlock_lv)) { + log_error("Failed to deativate %s.", + display_lvname(vg->sanlock_lv)); + return ECMD_FAILED; + } + } + + log_print_unless_silent("Volume group \"%s\" successfully changed", vg->name); + + return ECMD_PROCESSED; +} + +int vgchange_locktype_cmd(struct cmd_context *cmd, int argc, char **argv) +{ + struct processing_handle *handle; + const char *lock_type = arg_str_value(cmd, locktype_ARG, NULL); + const char *lockopt = arg_str_value(cmd, lockopt_ARG, NULL); + int ret; + + /* + * vgchange --locktype none --lockopt force VG + * + * This is a special/forced exception to change the lock type to none. + * It's needed for recovery cases and skips the normal steps of undoing + * the current lock type. It's a way to forcibly get access to a VG + * when the normal locking mechanisms are not working. + * + * It ignores: the current lvm locking config, lvmlockd, the state of + * the vg on other hosts, etc. It is meant to just remove any locking + * related metadata from the VG (cluster/lock_type flags, lock_type, + * lock_args). + * + * This can be necessary when manually recovering from certain failures. + * e.g. when a pv is lost containing the lvmlock lv (holding sanlock + * leases), the vg lock_type needs to be changed to none, and then + * back to sanlock, which recreates the lvmlock lv and leases. + * + * Set lockd_gl_disable, lockd_vg_disable, lockd_lv_disable to + * disable locking. lockd_gl(), lockd_vg() and lockd_lv() will + * just return success when they see the disable flag set. + */ + if (lockopt && !strcmp(lockopt, "force")) { + if (lock_type && strcmp(lock_type, "none")) { + log_error("Lock type can only be forced to \"none\" for recovery."); + return 0; + } + + if (!arg_is_set(cmd, yes_ARG) && + yes_no_prompt("Forcibly change VG lock type to none? [y/n]: ") == 'n') { + log_error("VG lock type not changed."); + return 0; + } + + cmd->lockd_gl_disable = 1; + cmd->lockd_vg_disable = 1; + cmd->lockd_lv_disable = 1; + cmd->handles_missing_pvs = 1; + cmd->force_access_clustered = 1; + goto process; + } + + if (!lvmlockd_use()) { + log_error("Using lock type requires lvmlockd."); + return 0; + } + + /* + * This is a special case where taking the global lock is + * not needed to protect global state, because the change is + * only to an existing VG. But, taking the global lock ex is + * helpful in this case to trigger a global cache validation + * on other hosts, to cause them to see the new system_id or + * lock_type. + */ + if (!lockd_global(cmd, "ex")) + return 0; + +process: + if (!(handle = init_processing_handle(cmd, NULL))) { + log_error("Failed to initialize processing handle."); + return ECMD_FAILED; + } + + ret = process_each_vg(cmd, argc, argv, NULL, NULL, READ_FOR_UPDATE, 0, handle, &_vgchange_locktype_single); + + destroy_processing_handle(cmd, handle); + return ret; +} + +static int _vgchange_lock_start_stop_single(struct cmd_context *cmd, const char *vg_name, + struct volume_group *vg, + struct processing_handle *handle) +{ + struct vgchange_params *vp = (struct vgchange_params *)handle->custom_handle; + + if (arg_is_set(cmd, lockstart_ARG)) { + if (!_vgchange_lock_start(cmd, vg, vp)) + return_ECMD_FAILED; + } else if (arg_is_set(cmd, lockstop_ARG)) { + if (!_vgchange_lock_stop(cmd, vg)) + return_ECMD_FAILED; + } + + return ECMD_PROCESSED; } + +int vgchange_lock_start_stop_cmd(struct cmd_context *cmd, int argc, char **argv) +{ + struct processing_handle *handle; + struct vgchange_params vp = { 0 }; + int ret; + + if (!lvmlockd_use()) { + log_error("Using lock start and lock stop requires lvmlockd."); + return 0; + } + + if (!(handle = init_processing_handle(cmd, NULL))) { + log_error("Failed to initialize processing handle."); + return ECMD_FAILED; + } + + if (arg_is_set(cmd, lockstop_ARG)) + cmd->lockd_vg_default_sh = 1; + + /* + * Starting lockspaces. For VGs not yet started, locks are not + * available to acquire, and for VGs already started, there's nothing + * to do, so disable VG locks. Try to acquire the global lock sh to + * validate the cache (if no gl is available, lockd_gl will force a + * cache validation). If the global lock is available, it can be + * benficial to hold sh to serialize lock-start with vgremove of the + * same VG from another host. + */ + if (arg_is_set(cmd, lockstart_ARG)) { + cmd->lockd_vg_disable = 1; + + if (!lockd_global(cmd, "sh")) + log_debug("No global lock for lock start"); + + /* Disable the lockd_gl in process_each_vg. */ + cmd->lockd_gl_disable = 1; + } else { + /* If the VG was started when it was exported, allow it to be stopped. */ + cmd->include_exported_vgs = 1; + } + + handle->custom_handle = &vp; + + ret = process_each_vg(cmd, argc, argv, NULL, NULL, 0, 0, handle, &_vgchange_lock_start_stop_single); + + /* Wait for lock-start ops that were initiated in vgchange_lockstart. */ + + if (arg_is_set(cmd, lockstart_ARG) && vp.lock_start_count) { + const char *start_opt = arg_str_value(cmd, lockopt_ARG, NULL); + + if (!lockd_global(cmd, "un")) + stack; + + if (!start_opt || !strcmp(start_opt, "auto")) { + if (vp.lock_start_sanlock) + log_print_unless_silent("Starting locking. Waiting for sanlock may take 20 sec to 3 min..."); + else + log_print_unless_silent("Starting locking. Waiting until locks are ready..."); + lockd_start_wait(cmd); + } else if (!strcmp(start_opt, "nowait") || !strcmp(start_opt, "autonowait")) { + log_print_unless_silent("Starting locking. VG can only be read until locks are ready."); + } + } + + destroy_processing_handle(cmd, handle); + return ret; +} + +static int _vgchange_systemid_single(struct cmd_context *cmd, const char *vg_name, + struct volume_group *vg, + struct processing_handle *handle) +{ + if (arg_is_set(cmd, majoritypvs_ARG)) { + struct pv_list *pvl; + int missing_pvs = 0; + int found_pvs = 0; + + dm_list_iterate_items(pvl, &vg->pvs) { + if (!pvl->pv->dev) + missing_pvs++; + else + found_pvs++; + } + if (found_pvs <= missing_pvs) { + log_error("Cannot change system ID without the majority of PVs (found %d of %d)", + found_pvs, found_pvs+missing_pvs); + return ECMD_FAILED; + } + } + + if (!_vgchange_system_id(cmd, vg)) + return_ECMD_FAILED; + + if (!vg_write(vg) || !vg_commit(vg)) + return_ECMD_FAILED; + + log_print_unless_silent("Volume group \"%s\" successfully changed", vg->name); + + return ECMD_PROCESSED; +} + +int vgchange_systemid_cmd(struct cmd_context *cmd, int argc, char **argv) +{ + struct processing_handle *handle; + int ret; + + /* + * This is a special case where taking the global lock is + * not needed to protect global state, because the change is + * only to an existing VG. But, taking the global lock ex is + * helpful in this case to trigger a global cache validation + * on other hosts, to cause them to see the new system_id or + * lock_type. + */ + if (!lockd_global(cmd, "ex")) + return 0; + + if (!(handle = init_processing_handle(cmd, NULL))) { + log_error("Failed to initialize processing handle."); + return ECMD_FAILED; + } + + if (arg_is_set(cmd, majoritypvs_ARG)) + cmd->handles_missing_pvs = 1; + + ret = process_each_vg(cmd, argc, argv, NULL, NULL, READ_FOR_UPDATE, 0, handle, &_vgchange_systemid_single); + + destroy_processing_handle(cmd, handle); + return ret; +} + |