summaryrefslogtreecommitdiff
path: root/tools/vgchange.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/vgchange.c')
-rw-r--r--tools/vgchange.c611
1 files changed, 572 insertions, 39 deletions
diff --git a/tools/vgchange.c b/tools/vgchange.c
index e5d700de8..cbdc29a3e 100644
--- a/tools/vgchange.c
+++ b/tools/vgchange.c
@@ -85,7 +85,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) {
@@ -114,11 +114,6 @@ static int _activate_lvs_in_vg(struct cmd_context *cmd, struct volume_group *vg,
if (lv_is_replicator_dev(lv) && (lv != first_replicator_dev(lv)))
continue;
- /* Can't deactivate a pvmove LV */
- /* FIXME There needs to be a controlled way of doing this */
- if (lv_is_pvmove(lv) && !is_change_activating(activate))
- continue;
-
if (lv_activation_skip(lv, activate, arg_count(cmd, ignoreactivationskip_ARG)))
continue;
@@ -147,14 +142,19 @@ static int _activate_lvs_in_vg(struct cmd_context *cmd, struct volume_group *vg,
}
sigint_restore();
- sync_local_dev_names(vg->cmd); /* Wait until devices are available */
+
+ /* 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;
+ }
if (expected_count)
log_verbose("%s %d logical volumes in volume group %s",
is_change_activating(activate) ?
"Activated" : "Deactivated", count, vg->name);
- return (expected_count != count) ? 0 : 1;
+ return (expected_count != count) ? 0 : r;
}
static int _vgchange_monitoring(struct cmd_context *cmd, struct volume_group *vg)
@@ -197,6 +197,19 @@ int vgchange_activate(struct cmd_context *cmd, struct volume_group *vg,
int do_activate = is_change_activating(activate);
/*
+ * 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 ECMD_FAILED;
+ }
+
+ /*
* Safe, since we never write out new metadata here. Required for
* partial activation to work.
*/
@@ -300,38 +313,74 @@ static int _vgchange_clustered(struct cmd_context *cmd,
struct volume_group *vg)
{
int clustered = arg_int_value(cmd, clustered_ARG, 0);
+ const char *lock_type = arg_str_value(cmd, locktype_ARG, NULL);
+ struct lv_list *lvl;
+ struct lv_segment *mirror_seg;
- if (clustered && (vg_is_clustered(vg))) {
- log_error("Volume group \"%s\" is already clustered",
- vg->name);
+ if (find_config_tree_bool(cmd, global_use_lvmlockd_CFG, NULL)) {
+ log_error("lvmlockd requires using the vgchange --lock-type option.");
return 0;
}
- if (!clustered && !(vg_is_clustered(vg))) {
- log_error("Volume group \"%s\" is already not clustered",
- vg->name);
- return 0;
+ if (lock_type && !strcmp(lock_type, "clvm"))
+ clustered = 1;
+
+ if (clustered && vg_is_clustered(vg)) {
+ if (vg->system_id && *vg->system_id)
+ log_warn("WARNING: Clearing invalid system ID %s from volume group %s.",
+ vg->system_id, vg->name);
+ else {
+ log_error("Volume group \"%s\" is already clustered", vg->name);
+ return 0;
+ }
+ }
+
+ if (!clustered && !vg_is_clustered(vg)) {
+ if ((!vg->system_id || !*vg->system_id) && cmd->system_id && *cmd->system_id)
+ log_warn("Setting missing system ID on Volume Group %s to %s.",
+ vg->name, cmd->system_id);
+ else {
+ log_error("Volume group \"%s\" is already not clustered",
+ vg->name);
+ return 0;
+ }
}
if (clustered && !arg_count(cmd, yes_ARG)) {
if (!clvmd_is_running()) {
- if (yes_no_prompt("LVM cluster daemon (clvmd) is not"
- " running.\n"
- "Make volume group \"%s\" clustered"
- " anyway? [y/n]: ", vg->name) == 'n') {
+ if (yes_no_prompt("LVM cluster daemon (clvmd) is not running. "
+ "Make volume group \"%s\" clustered "
+ "anyway? [y/n]: ", vg->name) == 'n') {
log_error("No volume groups changed.");
return 0;
}
} else if (!locking_is_clustered() &&
- (yes_no_prompt("LVM locking type is not clustered.\n"
- "Make volume group \"%s\" clustered"
- " anyway? [y/n]: ", vg->name) == 'n')) {
+ (yes_no_prompt("LVM locking type is not clustered. "
+ "Make volume group \"%s\" clustered "
+ "anyway? [y/n]: ", vg->name) == 'n')) {
log_error("No volume groups changed.");
return 0;
}
+#ifdef CMIRROR_REGION_COUNT_LIMIT
+ dm_list_iterate_items(lvl, &vg->lvs) {
+ if (!lv_is_mirror(lvl->lv))
+ continue;
+ mirror_seg = first_seg(lvl->lv);
+ if ((lvl->lv->size / mirror_seg->region_size) >
+ CMIRROR_REGION_COUNT_LIMIT) {
+ log_error("Unable to convert %s to clustered mode:"
+ " Mirror region size of %s is too small.",
+ vg->name, lvl->lv->name);
+ return 0;
+ }
+ }
+#endif
}
+ if (!vg_set_system_id(vg, clustered ? NULL : cmd->system_id))
+ return_0;
+
if (!vg_set_clustered(vg, clustered))
return_0;
@@ -471,9 +520,362 @@ static int _vgchange_profile(struct cmd_context *cmd,
return 1;
}
+static int _vgchange_locktype(struct cmd_context *cmd,
+ struct volume_group *vg)
+{
+ const char *lock_type = arg_str_value(cmd, locktype_ARG, NULL);
+ struct lv_list *lvl;
+ struct logical_volume *lv;
+ int lv_lock_count = 0;
+
+ /*
+ * 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.
+ */
+ if (!strcmp(lock_type, "none") && arg_is_set(cmd, force_ARG)) {
+ if (yes_no_prompt("Forcibly change VG %s lock type to none? [y/n]: ", vg->name) == 'n') {
+ log_error("VG lock type not changed.");
+ return 0;
+ }
+
+ 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 (!strcmp(vg->lock_type, lock_type)) {
+ log_warn("New lock_type %s matches the current lock_type %s.",
+ lock_type, vg->lock_type);
+ return 1;
+ }
+
+ /*
+ * When lvm is currently using clvm, this function is just an alternative
+ * to vgchange -c{y,n}, and can:
+ * - change none to clvm
+ * - change clvm to none
+ * - it CANNOT change to or from a lockd type
+ */
+ if (locking_is_clustered()) {
+ if (is_lockd_type(lock_type)) {
+ log_error("Changing to lock type %s requires lvmlockd.", lock_type);
+ return 0;
+ }
+
+ return _vgchange_clustered(cmd, vg);
+ }
+
+ /*
+ * 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;
+ }
+
+ /* none to clvm */
+ if (!strcmp(vg->lock_type, "none") && !strcmp(lock_type, "clvm")) {
+ log_warn("New clvm lock type will not be usable with lvmlockd.");
+ vg->status |= CLUSTERED;
+ vg->lock_type = "clvm"; /* this is optional */
+ return 1;
+ }
+
+ /* clvm to none */
+ if (!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
+ *
+ * To allow this, we need to do:
+ * lockd_stop_vg();
+ * lockd_free_vg_before();
+ * lockd_free_vg_after();
+ */
+ if (is_lockd_type(vg->lock_type)) {
+ /* FIXME: implement full undoing of the lock_type */
+ log_error("Changing VG %s from lock type %s not yet allowed.",
+ vg->name, vg->lock_type);
+ return 0;
+ }
+
+ /* ... to clvm */
+ if (!strcmp(lock_type, "clvm")) {
+ log_warn("New clvm lock type will not be usable with lvmlockd.");
+ vg->status |= CLUSTERED;
+ vg->lock_type = "clvm"; /* this is optional */
+ vg->system_id = NULL;
+ return 1;
+ }
+
+ /* ... 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;
+ }
+
+ log_error("Unknown lock type");
+ return 0;
+}
+
+/*
+ * 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);
+
+ /* FIXME Merge with vg_set_system_id() */
+ if (systemid_on_pvs(vg)) {
+ log_error("Metadata format %s does not support this type of system ID.",
+ vg->fid->fmt->name);
+ return 0;
+ }
+
+ 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_count(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_count(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;
+
+ if (vg->lvm1_system_id)
+ *vg->lvm1_system_id = '\0';
+
+ /* update system_id in lvmlockd's record for this vg */
+ if (!lockd_start_vg(cmd, vg))
+ log_debug("Failed to update lvmlockd.");
+
+ return 1;
+}
+
+static int _passes_lock_start_filter(struct cmd_context *cmd,
+ struct volume_group *vg,
+ const int cfg_id)
+{
+ 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;
+
+ /* 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)
+{
+ const char *start_opt = arg_str_value(cmd, lockopt_ARG, NULL);
+ int auto_opt = 0;
+
+ if (!is_lockd_type(vg->lock_type))
+ 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:
+ return lockd_start_vg(cmd, vg);
+}
+
+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,
- void *handle __attribute__((unused)))
+ struct processing_handle *handle __attribute__((unused)))
{
int ret = ECMD_PROCESSED;
unsigned i;
@@ -494,11 +896,14 @@ static int vgchange_single(struct cmd_context *cmd, const char *vg_name,
{ clustered_ARG, &_vgchange_clustered },
{ vgmetadatacopies_ARG, &_vgchange_metadata_copies },
{ metadataprofile_ARG, &_vgchange_profile },
- { profile_ARG, &_vgchange_profile},
- { detachprofile_ARG, &_vgchange_profile},
+ { profile_ARG, &_vgchange_profile },
+ { detachprofile_ARG, &_vgchange_profile },
+ { locktype_ARG, &_vgchange_locktype },
+ { systemid_ARG, &_vgchange_system_id },
};
- if (vg_is_exported(vg)) {
+ if (vg_is_exported(vg) &&
+ !(arg_is_set(cmd, lockstop_ARG) || arg_is_set(cmd, lockstart_ARG))) {
log_error("Volume group \"%s\" is exported", vg_name);
return ECMD_FAILED;
}
@@ -584,18 +989,116 @@ static int vgchange_single(struct cmd_context *cmd, const char *vg_name,
if (!_vgchange_background_polling(cmd, vg))
return_ECMD_FAILED;
+ if (arg_is_set(cmd, lockstart_ARG)) {
+ if (!_vgchange_lock_start(cmd, vg))
+ return_ECMD_FAILED;
+ } else if (arg_is_set(cmd, lockstop_ARG)) {
+ if (!_vgchange_lock_stop(cmd, vg))
+ return_ECMD_FAILED;
+ }
+
return ret;
}
+/*
+ * vgchange can do different things that require different
+ * locking, so look at each of those things here.
+ *
+ * Set up overrides for the default VG locking for various special cases.
+ * The VG lock will be acquired in process_each_vg.
+ *
+ * Acquire the gl lock according to which kind of vgchange command this is.
+ */
+
+static int _lockd_vgchange(struct cmd_context *cmd, int argc, char **argv)
+{
+ /* The default vg lock mode is ex, but these options only need sh. */
+
+ if (!lvmlockd_use() && arg_is_set(cmd, locktype_ARG)) {
+ log_error("Using lock type requires lvmlockd.");
+ return 0;
+ }
+
+ if (!lvmlockd_use() && (arg_is_set(cmd, lockstart_ARG) || arg_is_set(cmd, lockstop_ARG))) {
+ log_error("Using lock start and lock stop requires lvmlockd.");
+ return 0;
+ }
+
+ if (arg_is_set(cmd, activate_ARG) || arg_is_set(cmd, refresh_ARG)) {
+ 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;
+ }
+
+ /* Starting a vg lockspace means there are no locks available yet. */
+
+ if (arg_is_set(cmd, lockstart_ARG))
+ cmd->lockd_vg_disable = 1;
+
+ /*
+ * In most cases, lockd_vg does not apply when changing lock type.
+ * (We don't generally allow changing *from* lockd type yet.)
+ * lockd_vg could be called within _vgchange_locktype as needed.
+ */
+
+ if (arg_is_set(cmd, locktype_ARG))
+ cmd->lockd_vg_disable = 1;
+
+ /*
+ * Changing system_id or lock_type must only be done on explicitly
+ * named vgs.
+ */
+
+ if (arg_is_set(cmd, systemid_ARG) || arg_is_set(cmd, locktype_ARG))
+ cmd->command->flags &= ~ALL_VGS_IS_DEFAULT;
+
+ if (arg_is_set(cmd, lockstart_ARG)) {
+ /*
+ * The lockstart condition takes the global lock to serialize
+ * with any other host that tries to remove the VG while this
+ * tries to start it. (Zero argc means all VGs, in wich case
+ * process_each_vg will acquire the global lock.)
+ */
+ if (argc && !lockd_gl(cmd, "sh", 0))
+ return_ECMD_FAILED;
+
+ } else if (arg_is_set(cmd, systemid_ARG) || arg_is_set(cmd, locktype_ARG)) {
+ /*
+ * 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_gl(cmd, "ex", LDGL_UPDATE_NAMES))
+ return_ECMD_FAILED;
+ }
+
+ return 1;
+}
+
int vgchange(struct cmd_context *cmd, int argc, char **argv)
{
- /* Update commands that can be combined */
+ uint32_t flags = 0;
+ int ret;
+
+ int noupdate =
+ arg_count(cmd, activate_ARG) ||
+ arg_count(cmd, lockstart_ARG) ||
+ arg_count(cmd, lockstop_ARG) ||
+ arg_count(cmd, monitor_ARG) ||
+ arg_count(cmd, poll_ARG) ||
+ arg_count(cmd, refresh_ARG);
+
int update_partial_safe =
arg_count(cmd, deltag_ARG) ||
arg_count(cmd, addtag_ARG) ||
arg_count(cmd, metadataprofile_ARG) ||
arg_count(cmd, profile_ARG) ||
arg_count(cmd, detachprofile_ARG);
+
int update_partial_unsafe =
arg_count(cmd, logicalvolume_ARG) ||
arg_count(cmd, maxphysicalvolumes_ARG) ||
@@ -604,18 +1107,14 @@ int vgchange(struct cmd_context *cmd, int argc, char **argv)
arg_count(cmd, physicalextentsize_ARG) ||
arg_count(cmd, clustered_ARG) ||
arg_count(cmd, alloc_ARG) ||
- arg_count(cmd, vgmetadatacopies_ARG);
+ arg_count(cmd, vgmetadatacopies_ARG) ||
+ arg_count(cmd, locktype_ARG) ||
+ arg_count(cmd, systemid_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;
}
@@ -705,6 +1204,40 @@ int vgchange(struct cmd_context *cmd, int argc, char **argv)
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);
+ /*
+ * 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;
+
+ if (!_lockd_vgchange(cmd, argc, argv))
+ return_ECMD_FAILED;
+
+ if (update)
+ flags |= READ_FOR_UPDATE;
+ if (arg_is_set(cmd, lockstart_ARG) || arg_is_set(cmd, lockstop_ARG))
+ flags |= READ_ALLOW_EXPORTED;
+
+ ret = process_each_vg(cmd, argc, argv, flags, NULL, &vgchange_single);
+
+ /* Wait for lock-start ops that were initiated in vgchange_lockstart. */
+
+ if (arg_is_set(cmd, lockstart_ARG)) {
+ const char *start_opt = arg_str_value(cmd, lockopt_ARG, NULL);
+
+ if (!lockd_gl(cmd, "un", 0))
+ stack;
+
+ if (!start_opt || !strcmp(start_opt, "auto")) {
+ 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.");
+ }
+ }
+
+ return ret;
}