Blame SOURCES/0004-pvscan-add-options-listlvs-listvg-checkcomplete.patch

9db2f0
From 9273d0cb758b38c17dc11e344067c71f79d12f02 Mon Sep 17 00:00:00 2001
9db2f0
From: David Teigland <teigland@redhat.com>
9db2f0
Date: Wed, 9 Dec 2020 10:59:40 -0600
9db2f0
Subject: [PATCH 04/11] pvscan: add options listlvs listvg checkcomplete
9db2f0
9db2f0
pvscan --cache <dev>
9db2f0
    . read only dev
9db2f0
    . create online file for dev
9db2f0
9db2f0
pvscan --listvg <dev>
9db2f0
    . read only dev
9db2f0
    . list VG using dev
9db2f0
9db2f0
pvscan --listlvs <dev>
9db2f0
    . read only dev
9db2f0
    . list VG using dev
9db2f0
    . list LVs using dev
9db2f0
9db2f0
pvscan --cache --listvg [--checkcomplete] <dev>
9db2f0
    . read only dev
9db2f0
    . create online file for dev
9db2f0
    . list VG using dev
9db2f0
    . [check online files and report if VG is complete]
9db2f0
9db2f0
pvscan --cache --listlvs [--checkcomplete] <dev>
9db2f0
    . read only dev
9db2f0
    . create online file for dev
9db2f0
    . list VG using dev
9db2f0
    . list LVs using dev
9db2f0
    . [check online files and report if VG is complete]
9db2f0
    . [check online files and report if LVs are complete]
9db2f0
9db2f0
[--vgonline]
9db2f0
can be used with --checkcomplete, to enable use of a vg online
9db2f0
file.  This results in only the first pvscan command to see
9db2f0
the complete VG to report 'VG complete', and others will report
9db2f0
'VG finished'.  This allows the caller to easily run a single
9db2f0
activation of the VG.
9db2f0
9db2f0
[--udevoutput]
9db2f0
can be used with --cache --listvg --checkcomplete, to enable
9db2f0
an output mode that prints LVM_VG_NAME_COMPLETE='vgname' that
9db2f0
a udev rule can import, and prevents other output from the
9db2f0
command (other output causes udev to ignore the command.)
9db2f0
9db2f0
The list of complete LVs is meant to be passed to lvchange -aay,
9db2f0
or the complete VG used with vgchange -aay.
9db2f0
9db2f0
When --checkcomplete is used, lvm assumes that that the output
9db2f0
will be used to trigger event-based autoactivation, so the pvscan
9db2f0
does nothing if event_activation=0 and --checkcomplete is used.
9db2f0
9db2f0
Example of listlvs
9db2f0
------------------
9db2f0
9db2f0
$ lvs -a vg -olvname,devices
9db2f0
  LV     Devices
9db2f0
  lv_a   /dev/loop0(0)
9db2f0
  lv_ab  /dev/loop0(1),/dev/loop1(1)
9db2f0
  lv_abc /dev/loop0(3),/dev/loop1(3),/dev/loop2(1)
9db2f0
  lv_b   /dev/loop1(0)
9db2f0
  lv_c   /dev/loop2(0)
9db2f0
9db2f0
$ pvscan --cache --listlvs --checkcomplete /dev/loop0
9db2f0
  pvscan[35680] PV /dev/loop0 online, VG vg incomplete (need 2).
9db2f0
  VG vg incomplete
9db2f0
  LV vg/lv_a complete
9db2f0
  LV vg/lv_ab incomplete
9db2f0
  LV vg/lv_abc incomplete
9db2f0
9db2f0
$ pvscan --cache --listlvs --checkcomplete /dev/loop1
9db2f0
  pvscan[35681] PV /dev/loop1 online, VG vg incomplete (need 1).
9db2f0
  VG vg incomplete
9db2f0
  LV vg/lv_b complete
9db2f0
  LV vg/lv_ab complete
9db2f0
  LV vg/lv_abc incomplete
9db2f0
9db2f0
$ pvscan --cache --listlvs --checkcomplete /dev/loop2
9db2f0
  pvscan[35682] PV /dev/loop2 online, VG vg is complete.
9db2f0
  VG vg complete
9db2f0
  LV vg/lv_c complete
9db2f0
  LV vg/lv_abc complete
9db2f0
9db2f0
Example of listvg
9db2f0
-----------------
9db2f0
9db2f0
$ pvscan --cache --listvg --checkcomplete /dev/loop0
9db2f0
  pvscan[35684] PV /dev/loop0 online, VG vg incomplete (need 2).
9db2f0
  VG vg incomplete
9db2f0
9db2f0
$ pvscan --cache --listvg --checkcomplete /dev/loop1
9db2f0
  pvscan[35685] PV /dev/loop1 online, VG vg incomplete (need 1).
9db2f0
  VG vg incomplete
9db2f0
9db2f0
$ pvscan --cache --listvg --checkcomplete /dev/loop2
9db2f0
  pvscan[35686] PV /dev/loop2 online, VG vg is complete.
9db2f0
  VG vg complete
9db2f0
---
9db2f0
 lib/commands/toolcontext.h |   1 +
9db2f0
 lib/metadata/metadata.c    |  33 ++++
9db2f0
 lib/metadata/metadata.h    |   4 +
9db2f0
 tools/args.h               |  20 +++
9db2f0
 tools/command-lines.in     |  30 +++-
9db2f0
 tools/lvmcmdline.c         |   4 +
9db2f0
 tools/pvscan.c             | 418 ++++++++++++++++++++++++++++++++++-----------
9db2f0
 tools/toollib.c            |  18 ++
9db2f0
 tools/tools.h              |   2 +
9db2f0
 9 files changed, 431 insertions(+), 99 deletions(-)
9db2f0
9db2f0
diff --git a/lib/commands/toolcontext.h b/lib/commands/toolcontext.h
9db2f0
index 0911b05..0cb4ad7 100644
9db2f0
--- a/lib/commands/toolcontext.h
9db2f0
+++ b/lib/commands/toolcontext.h
9db2f0
@@ -29,6 +29,7 @@ struct config_info {
9db2f0
 	int debug_classes;
9db2f0
 	int verbose;
9db2f0
 	int silent;
9db2f0
+	int suppress;
9db2f0
 	int test;
9db2f0
 	int syslog;
9db2f0
 	int activation;
9db2f0
diff --git a/lib/metadata/metadata.c b/lib/metadata/metadata.c
9db2f0
index 0cbf678..ef343da 100644
9db2f0
--- a/lib/metadata/metadata.c
9db2f0
+++ b/lib/metadata/metadata.c
9db2f0
@@ -5279,3 +5279,36 @@ struct volume_group *vg_read_for_update(struct cmd_context *cmd, const char *vg_
9db2f0
 
9db2f0
 	return vg;
9db2f0
 }
9db2f0
+
9db2f0
+int get_visible_lvs_using_pv(struct cmd_context *cmd, struct volume_group *vg, struct device *dev,
9db2f0
+			     struct dm_list *lvs_list)
9db2f0
+{
9db2f0
+	struct pv_list *pvl;
9db2f0
+	struct lv_list *lvl, *lvl2;
9db2f0
+	struct physical_volume *pv = NULL;
9db2f0
+
9db2f0
+	dm_list_iterate_items(pvl, &vg->pvs) {
9db2f0
+		if (pvl->pv->dev == dev) {
9db2f0
+			pv = pvl->pv;
9db2f0
+			break;
9db2f0
+		}
9db2f0
+	}
9db2f0
+
9db2f0
+	if (!pv)
9db2f0
+		return_0;
9db2f0
+
9db2f0
+	dm_list_iterate_items(lvl, &vg->lvs) {
9db2f0
+		if (!lv_is_visible(lvl->lv))
9db2f0
+			continue;
9db2f0
+		if (!lv_is_on_pv(lvl->lv, pv))
9db2f0
+			continue;
9db2f0
+
9db2f0
+		if (!(lvl2 = dm_pool_zalloc(cmd->mem, sizeof(*lvl2))))
9db2f0
+			return_0;
9db2f0
+		lvl2->lv = lvl->lv;
9db2f0
+		dm_list_add(lvs_list, &lvl2->list);
9db2f0
+	}
9db2f0
+
9db2f0
+	return 1;
9db2f0
+}
9db2f0
+
9db2f0
diff --git a/lib/metadata/metadata.h b/lib/metadata/metadata.h
9db2f0
index dfd576e..70f7bbc 100644
9db2f0
--- a/lib/metadata/metadata.h
9db2f0
+++ b/lib/metadata/metadata.h
9db2f0
@@ -538,4 +538,8 @@ char *tags_format_and_copy(struct dm_pool *mem, const struct dm_list *tagsl);
9db2f0
 
9db2f0
 void set_pv_devices(struct format_instance *fid, struct volume_group *vg);
9db2f0
 
9db2f0
+int get_visible_lvs_using_pv(struct cmd_context *cmd, struct volume_group *vg, struct device *dev,
9db2f0
+                            struct dm_list *lvs_list);
9db2f0
+
9db2f0
+
9db2f0
 #endif
9db2f0
diff --git a/tools/args.h b/tools/args.h
9db2f0
index d4f23f8..9aeec40 100644
9db2f0
--- a/tools/args.h
9db2f0
+++ b/tools/args.h
9db2f0
@@ -329,6 +329,19 @@ arg(labelsector_ARG, '\0', "labelsector", number_VAL, 0, 0,
9db2f0
     "start of the disk (between 0 and 3 inclusive - see LABEL_SCAN_SECTORS\n"
9db2f0
     "in the source). Use with care.\n")
9db2f0
 
9db2f0
+arg(listlvs_ARG, '\0', "listlvs", 0, 0, 0,
9db2f0
+    "Print a list of LVs that use the device.\n")
9db2f0
+
9db2f0
+arg(listvg_ARG, '\0', "listvg", 0, 0, 0,
9db2f0
+    "Print the VG that uses the device.\n")
9db2f0
+
9db2f0
+arg(checkcomplete_ARG, '\0', "checkcomplete", 0, 0, 0,
9db2f0
+    "Check if all the devices used by a VG or LV are present,\n"
9db2f0
+    "and print \"complete\" or \"incomplete\" for each listed\n"
9db2f0
+    "VG or LV.  This option is used as a part of event-based\n"
9db2f0
+    "autoactivation, so pvscan will do nothing if this option\n"
9db2f0
+    "is set and event_activation=0 in the config settings.\n")
9db2f0
+
9db2f0
 arg(lockopt_ARG, '\0', "lockopt", string_VAL, 0, 0,
9db2f0
     "Used to pass options for special cases to lvmlockd.\n"
9db2f0
     "See \\fBlvmlockd\\fP(8) for more information.\n")
9db2f0
@@ -811,6 +824,9 @@ arg(type_ARG, '\0', "type", segtype_VAL, 0, 0,
9db2f0
     "(e.g. --stripes, --mirrors, --snapshot, --virtualsize, --thin, --cache, --vdo).\n"
9db2f0
     "Use inferred types with care because it can lead to unexpected results.\n")
9db2f0
 
9db2f0
+arg(udevoutput_ARG, '\0', "udevoutput", 0, 0, 0,
9db2f0
+    "Command output is modified to be imported from a udev rule.\n")
9db2f0
+
9db2f0
 arg(unbuffered_ARG, '\0', "unbuffered", 0, 0, 0,
9db2f0
     "Produce output immediately without sorting or aligning the columns properly.\n")
9db2f0
 
9db2f0
@@ -887,6 +903,10 @@ arg(vgmetadatacopies_ARG, '\0', "vgmetadatacopies", vgmetadatacopies_VAL, 0, 0,
9db2f0
     "\\fBall\\fP causes LVM to first clear the metadataignore flags on\n"
9db2f0
     "all PVs, and then to become unmanaged.\n")
9db2f0
 
9db2f0
+arg(vgonline_ARG, '\0', "vgonline", 0, 0, 0,
9db2f0
+    "The first command to see a complete VG will report it uniquely.\n"
9db2f0
+    "Other commands to see the complete VG will report it differently.\n")
9db2f0
+
9db2f0
 arg(withsummary_ARG, '\0', "withsummary", 0, 0, 0,
9db2f0
     "Display a one line comment for each configuration node.\n")
9db2f0
 
9db2f0
diff --git a/tools/command-lines.in b/tools/command-lines.in
9db2f0
index 0bc5a49..aa3e3d9 100644
9db2f0
--- a/tools/command-lines.in
9db2f0
+++ b/tools/command-lines.in
9db2f0
@@ -1633,11 +1633,37 @@ DESC: Display PV information.
9db2f0
 
9db2f0
 pvscan --cache_long
9db2f0
 OO: --ignorelockingfailure, --reportformat ReportFmt,
9db2f0
---activate ay, --major Number, --minor Number, --noudevsync
9db2f0
+--major Number, --minor Number, --noudevsync
9db2f0
 OP: PV|String ...
9db2f0
 IO: --background
9db2f0
 ID: pvscan_cache
9db2f0
-DESC: Autoactivate a VG when all PVs are online.
9db2f0
+DESC: Record that a PV is online or offline.
9db2f0
+
9db2f0
+pvscan --cache_long --activate ay
9db2f0
+OO: --ignorelockingfailure, --reportformat ReportFmt,
9db2f0
+--major Number, --minor Number, --noudevsync
9db2f0
+OP: PV|String ...
9db2f0
+IO: --background
9db2f0
+ID: pvscan_cache
9db2f0
+DESC: Record that a PV is online and autoactivate the VG if complete.
9db2f0
+
9db2f0
+pvscan --cache_long --listvg PV
9db2f0
+OO: --ignorelockingfailure, --checkcomplete, --vgonline, --udevoutput
9db2f0
+ID: pvscan_cache
9db2f0
+DESC: Record that a PV is online and list the VG using the PV.
9db2f0
+
9db2f0
+pvscan --cache_long --listlvs PV
9db2f0
+OO: --ignorelockingfailure, --checkcomplete, --vgonline
9db2f0
+ID: pvscan_cache
9db2f0
+DESC: Record that a PV is online and list LVs using the PV.
9db2f0
+
9db2f0
+pvscan --listlvs PV
9db2f0
+ID: pvscan_cache
9db2f0
+DESC: List LVs using the PV.
9db2f0
+
9db2f0
+pvscan --listvg PV
9db2f0
+ID: pvscan_cache
9db2f0
+DESC: List the VG using the PV.
9db2f0
 
9db2f0
 ---
9db2f0
 
9db2f0
diff --git a/tools/lvmcmdline.c b/tools/lvmcmdline.c
9db2f0
index 4b63d48..6ea5487 100644
9db2f0
--- a/tools/lvmcmdline.c
9db2f0
+++ b/tools/lvmcmdline.c
9db2f0
@@ -2390,6 +2390,9 @@ static void _reset_current_settings_to_default(struct cmd_context *cmd)
9db2f0
 
9db2f0
 static void _get_current_output_settings_from_args(struct cmd_context *cmd)
9db2f0
 {
9db2f0
+	if (arg_is_set(cmd, udevoutput_ARG))
9db2f0
+		cmd->current_settings.suppress = 1;
9db2f0
+
9db2f0
 	if (arg_is_set(cmd, debug_ARG))
9db2f0
 		cmd->current_settings.debug = _LOG_FATAL + (arg_count(cmd, debug_ARG) - 1);
9db2f0
 
9db2f0
@@ -2405,6 +2408,7 @@ static void _get_current_output_settings_from_args(struct cmd_context *cmd)
9db2f0
 
9db2f0
 static void _apply_current_output_settings(struct cmd_context *cmd)
9db2f0
 {
9db2f0
+	log_suppress(cmd->current_settings.suppress);
9db2f0
 	init_debug(cmd->current_settings.debug);
9db2f0
 	init_debug_classes_logged(cmd->default_settings.debug_classes);
9db2f0
 	init_verbose(cmd->current_settings.verbose + VERBOSE_BASE_LEVEL);
9db2f0
diff --git a/tools/pvscan.c b/tools/pvscan.c
9db2f0
index 2299890..a836e0a 100644
9db2f0
--- a/tools/pvscan.c
9db2f0
+++ b/tools/pvscan.c
9db2f0
@@ -179,6 +179,27 @@ out:
9db2f0
 	return ret;
9db2f0
 }
9db2f0
 
9db2f0
+/*
9db2f0
+ * Avoid a duplicate pvscan[%d] prefix when logging to the journal.
9db2f0
+ * FIXME: this should probably replace if (udevoutput) with
9db2f0
+ * if (log_journal & LOG_JOURNAL_OUTPUT)
9db2f0
+ */
9db2f0
+#define log_print_pvscan(cmd, fmt, args...) \
9db2f0
+do \
9db2f0
+	if (arg_is_set(cmd, udevoutput_ARG)) \
9db2f0
+		log_print(fmt, ##args); \
9db2f0
+	else \
9db2f0
+		log_print("pvscan[%d] " fmt, getpid(), ##args); \
9db2f0
+while (0)
9db2f0
+
9db2f0
+#define log_error_pvscan(cmd, fmt, args...) \
9db2f0
+do \
9db2f0
+	if (arg_is_set(cmd, udevoutput_ARG)) \
9db2f0
+		log_error(fmt, ##args); \
9db2f0
+	else \
9db2f0
+		log_error("pvscan[%d] " fmt, getpid(), ##args); \
9db2f0
+while (0)
9db2f0
+
9db2f0
 static char *_vgname_in_pvid_file_buf(char *buf)
9db2f0
 {
9db2f0
 	char *p, *n;
9db2f0
@@ -259,7 +280,7 @@ static void _lookup_file_remove(char *vgname)
9db2f0
  * that the vg will be activated again when it becomes complete.
9db2f0
  */
9db2f0
 
9db2f0
-static void _online_vg_file_remove(const char *vgname)
9db2f0
+void online_vg_file_remove(const char *vgname)
9db2f0
 {
9db2f0
 	char path[PATH_MAX];
9db2f0
 
9db2f0
@@ -314,7 +335,7 @@ static void _online_pvid_file_remove_devno(int major, int minor)
9db2f0
 				log_sys_debug("unlink", path);
9db2f0
 
9db2f0
 			if (file_vgname[0]) {
9db2f0
-				_online_vg_file_remove(file_vgname);
9db2f0
+				online_vg_file_remove(file_vgname);
9db2f0
 				_lookup_file_remove(file_vgname);
9db2f0
 			}
9db2f0
 		}
9db2f0
@@ -345,7 +366,7 @@ static void _online_files_remove(const char *dirpath)
9db2f0
 		log_sys_debug("closedir", dirpath);
9db2f0
 }
9db2f0
 
9db2f0
-static int _online_pvid_file_create(struct device *dev, const char *vgname)
9db2f0
+static int _online_pvid_file_create(struct cmd_context *cmd, struct device *dev, const char *vgname)
9db2f0
 {
9db2f0
 	char path[PATH_MAX];
9db2f0
 	char buf[MAX_PVID_FILE_SIZE] = { 0 };
9db2f0
@@ -362,18 +383,18 @@ static int _online_pvid_file_create(struct device *dev, const char *vgname)
9db2f0
 	minor = (int)MINOR(dev->dev);
9db2f0
 
9db2f0
 	if (dm_snprintf(path, sizeof(path), "%s/%s", _pvs_online_dir, dev->pvid) < 0) {
9db2f0
-		log_error("Path %s/%s is too long.", _pvs_online_dir, dev->pvid);
9db2f0
+		log_error_pvscan(cmd, "Path %s/%s is too long.", _pvs_online_dir, dev->pvid);
9db2f0
 		return 0;
9db2f0
 	}
9db2f0
 
9db2f0
 	if ((len1 = dm_snprintf(buf, sizeof(buf), "%d:%d\n", major, minor)) < 0) {
9db2f0
-		log_error("Cannot create online file path for %s %d:%d.", dev_name(dev), major, minor);
9db2f0
+		log_error_pvscan(cmd, "Cannot create online file path for %s %d:%d.", dev_name(dev), major, minor);
9db2f0
 		return 0;
9db2f0
 	}
9db2f0
 
9db2f0
 	if (vgname) {
9db2f0
 		if ((len2 = dm_snprintf(buf + len1, sizeof(buf) - len1, "vg:%s\n", vgname)) < 0) {
9db2f0
-			log_warn("Incomplete online file for %s %d:%d vg %s.", dev_name(dev), major, minor, vgname);
9db2f0
+			log_print_pvscan(cmd, "Incomplete online file for %s %d:%d vg %s.", dev_name(dev), major, minor, vgname);
9db2f0
 			/* can still continue without vgname */
9db2f0
 			len2 = 0;
9db2f0
 		}
9db2f0
@@ -387,7 +408,7 @@ static int _online_pvid_file_create(struct device *dev, const char *vgname)
9db2f0
 	if (fd < 0) {
9db2f0
 		if (errno == EEXIST)
9db2f0
 			goto check_duplicate;
9db2f0
-		log_error("Failed to create online file for %s path %s error %d", dev_name(dev), path, errno);
9db2f0
+		log_error_pvscan(cmd, "Failed to create online file for %s path %s error %d", dev_name(dev), path, errno);
9db2f0
 		return 0;
9db2f0
 	}
9db2f0
 
9db2f0
@@ -435,12 +456,12 @@ check_duplicate:
9db2f0
 	/* Don't know how vgname might not match, but it's not good so fail. */
9db2f0
 
9db2f0
 	if ((file_major != major) || (file_minor != minor))
9db2f0
-		log_error("pvscan[%d] PV %s is duplicate for PVID %s on %d:%d and %d:%d.",
9db2f0
-			  getpid(), dev_name(dev), dev->pvid, major, minor, file_major, file_minor);
9db2f0
+		log_error_pvscan(cmd, "PV %s is duplicate for PVID %s on %d:%d and %d:%d.",
9db2f0
+			         dev_name(dev), dev->pvid, major, minor, file_major, file_minor);
9db2f0
 
9db2f0
 	if (file_vgname[0] && vgname && strcmp(file_vgname, vgname))
9db2f0
-		log_error("pvscan[%d] PV %s has unexpected VG %s vs %s.",
9db2f0
-			  getpid(), dev_name(dev), vgname, file_vgname);
9db2f0
+		log_error_pvscan(cmd, "PV %s has unexpected VG %s vs %s.",
9db2f0
+			         dev_name(dev), vgname, file_vgname);
9db2f0
 
9db2f0
 	return 0;
9db2f0
 }
9db2f0
@@ -475,7 +496,7 @@ static int _write_lookup_file(struct cmd_context *cmd, struct volume_group *vg)
9db2f0
 	int fd;
9db2f0
 
9db2f0
 	if (dm_snprintf(path, sizeof(path), "%s/%s", _pvs_lookup_dir, vg->name) < 0) {
9db2f0
-		log_error("Path %s/%s is too long.", _pvs_lookup_dir, vg->name);
9db2f0
+		log_error_pvscan(cmd, "Path %s/%s is too long.", _pvs_lookup_dir, vg->name);
9db2f0
 		return 0;
9db2f0
 	}
9db2f0
 
9db2f0
@@ -638,7 +659,7 @@ static int _count_pvid_files_from_lookup_file(struct cmd_context *cmd, struct de
9db2f0
 	return (vgname) ? 1 : 0;
9db2f0
 }
9db2f0
 
9db2f0
-static void _online_dir_setup(void)
9db2f0
+static void _online_dir_setup(struct cmd_context *cmd)
9db2f0
 {
9db2f0
 	struct stat st;
9db2f0
 	int rv;
9db2f0
@@ -652,7 +673,7 @@ static void _online_dir_setup(void)
9db2f0
 	dm_prepare_selinux_context(NULL, 0);
9db2f0
 
9db2f0
 	if ((rv < 0) && stat(DEFAULT_RUN_DIR, &st))
9db2f0
-		log_error("Failed to create %s %d", DEFAULT_RUN_DIR, errno);
9db2f0
+		log_error_pvscan(cmd, "Failed to create %s %d", DEFAULT_RUN_DIR, errno);
9db2f0
 
9db2f0
 do_pvs:
9db2f0
 	if (!stat(_pvs_online_dir, &st))
9db2f0
@@ -664,7 +685,7 @@ do_pvs:
9db2f0
 	dm_prepare_selinux_context(NULL, 0);
9db2f0
 
9db2f0
 	if ((rv < 0) && stat(_pvs_online_dir, &st))
9db2f0
-		log_error("Failed to create %s %d", _pvs_online_dir, errno);
9db2f0
+		log_error_pvscan(cmd, "Failed to create %s %d", _pvs_online_dir, errno);
9db2f0
 
9db2f0
 do_vgs:
9db2f0
 	if (!stat(_vgs_online_dir, &st))
9db2f0
@@ -676,7 +697,7 @@ do_vgs:
9db2f0
 	dm_prepare_selinux_context(NULL, 0);
9db2f0
 
9db2f0
 	if ((rv < 0) && stat(_vgs_online_dir, &st))
9db2f0
-		log_error("Failed to create %s %d", _vgs_online_dir, errno);
9db2f0
+		log_error_pvscan(cmd, "Failed to create %s %d", _vgs_online_dir, errno);
9db2f0
 
9db2f0
 do_lookup:
9db2f0
 	if (!stat(_pvs_lookup_dir, &st))
9db2f0
@@ -688,7 +709,7 @@ do_lookup:
9db2f0
 	dm_prepare_selinux_context(NULL, 0);
9db2f0
 
9db2f0
 	if ((rv < 0) && stat(_pvs_lookup_dir, &st))
9db2f0
-		log_error("Failed to create %s %d", _pvs_lookup_dir, errno);
9db2f0
+		log_error_pvscan(cmd, "Failed to create %s %d", _pvs_lookup_dir, errno);
9db2f0
 
9db2f0
 
9db2f0
 }
9db2f0
@@ -725,7 +746,7 @@ static int _pvscan_aa_single(struct cmd_context *cmd, const char *vg_name,
9db2f0
 	log_debug("pvscan autoactivating VG %s.", vg_name);
9db2f0
 
9db2f0
 	if (!vgchange_activate(cmd, vg, CHANGE_AAY)) {
9db2f0
-		log_error("%s: autoactivation failed.", vg->name);
9db2f0
+		log_error_pvscan(cmd, "%s: autoactivation failed.", vg->name);
9db2f0
 		pp->activate_errors++;
9db2f0
 	}
9db2f0
 
9db2f0
@@ -738,7 +759,7 @@ static int _online_vg_file_create(struct cmd_context *cmd, const char *vgname)
9db2f0
 	int fd;
9db2f0
 
9db2f0
 	if (dm_snprintf(path, sizeof(path), "%s/%s", _vgs_online_dir, vgname) < 0) {
9db2f0
-		log_error("Path %s/%s is too long.", _vgs_online_dir, vgname);
9db2f0
+		log_error_pvscan(cmd, "Path %s/%s is too long.", _vgs_online_dir, vgname);
9db2f0
 		return 0;
9db2f0
 	}
9db2f0
 
9db2f0
@@ -826,15 +847,15 @@ static int _get_devs_from_saved_vg(struct cmd_context *cmd, const char *vgname,
9db2f0
 		_online_pvid_file_read(path, &file_major, &file_minor, file_vgname);
9db2f0
 
9db2f0
 		if (file_vgname[0] && strcmp(vgname, file_vgname)) {
9db2f0
-			log_error("Wrong VG found for %d:%d PVID %s: %s vs %s",
9db2f0
-				  file_major, file_minor, pvid, vgname, file_vgname);
9db2f0
+			log_error_pvscan(cmd, "Wrong VG found for %d:%d PVID %s: %s vs %s",
9db2f0
+				         file_major, file_minor, pvid, vgname, file_vgname);
9db2f0
 			goto bad;
9db2f0
 		}
9db2f0
 
9db2f0
 		devno = MKDEV(file_major, file_minor);
9db2f0
 
9db2f0
 		if (!(dev = dev_cache_get_by_devt(cmd, devno, NULL, NULL))) {
9db2f0
-			log_error("No device found for %d:%d PVID %s", file_major, file_minor, pvid);
9db2f0
+			log_error_pvscan(cmd, "No device found for %d:%d PVID %s", file_major, file_minor, pvid);
9db2f0
 			goto bad;
9db2f0
 		}
9db2f0
 
9db2f0
@@ -844,7 +865,7 @@ static int _get_devs_from_saved_vg(struct cmd_context *cmd, const char *vgname,
9db2f0
 		if (strcmp(name1, name2)) {
9db2f0
 			if (!id_write_format((const struct id *)pvid, uuidstr, sizeof(uuidstr)))
9db2f0
 				uuidstr[0] = '\0';
9db2f0
-			log_print("PVID %s read from %s last written to %s.", uuidstr, name1, name2);
9db2f0
+			log_print_pvscan(cmd, "PVID %s read from %s last written to %s.", uuidstr, name1, name2);
9db2f0
 			goto bad;
9db2f0
 		}
9db2f0
 
9db2f0
@@ -921,7 +942,7 @@ static int _pvscan_aa_quick(struct cmd_context *cmd, struct pvscan_aa_params *pp
9db2f0
 	 * The dev_cache gives us struct devices from the devnums.
9db2f0
 	 */
9db2f0
 	if (!_get_devs_from_saved_vg(cmd, vgname, &devs)) {
9db2f0
-		log_print("pvscan[%d] VG %s not using quick activation.", getpid(), vgname);
9db2f0
+		log_print_pvscan(cmd, "VG %s not using quick activation.", vgname);
9db2f0
 		*no_quick = 1;
9db2f0
 		return ECMD_FAILED;
9db2f0
 	}
9db2f0
@@ -940,7 +961,7 @@ static int _pvscan_aa_quick(struct cmd_context *cmd, struct pvscan_aa_params *pp
9db2f0
 	 * label rescan are then disabled in vg_read.)
9db2f0
 	 */
9db2f0
 	if (!lock_vol(cmd, vgname, LCK_VG_WRITE, NULL)) {
9db2f0
-		log_error("pvscan activation for VG %s failed to lock VG.", vgname);
9db2f0
+		log_error_pvscan(cmd, "activation for VG %s failed to lock VG.", vgname);
9db2f0
 		return ECMD_FAILED;
9db2f0
 	}
9db2f0
 
9db2f0
@@ -953,7 +974,7 @@ static int _pvscan_aa_quick(struct cmd_context *cmd, struct pvscan_aa_params *pp
9db2f0
 	label_scan_devs(cmd, NULL, &devs);
9db2f0
 
9db2f0
 	if (!(vgid = lvmcache_vgid_from_vgname(cmd, vgname))) {
9db2f0
-		log_error("pvscan activation for VG %s failed to find vgid.", vgname);
9db2f0
+		log_error_pvscan(cmd, "activation for VG %s failed to find vgid.", vgname);
9db2f0
 		return ECMD_FAILED;
9db2f0
 	}
9db2f0
 
9db2f0
@@ -973,7 +994,7 @@ static int _pvscan_aa_quick(struct cmd_context *cmd, struct pvscan_aa_params *pp
9db2f0
 		 * original device arg scan.  There will be very few and unusual
9db2f0
 		 * cases that would be caught here.
9db2f0
 		 */
9db2f0
-		log_error("pvscan activation for VG %s cannot read (%x).", vgname, error_flags);
9db2f0
+		log_error_pvscan(cmd, "activation for VG %s cannot read (%x).", vgname, error_flags);
9db2f0
 		return ECMD_FAILED;
9db2f0
 	}
9db2f0
 
9db2f0
@@ -995,7 +1016,7 @@ static int _pvscan_aa_quick(struct cmd_context *cmd, struct pvscan_aa_params *pp
9db2f0
 	dm_list_iterate_items(pvl, &vg->pvs) {
9db2f0
 		if (dev_in_device_list(pvl->pv->dev, &devs))
9db2f0
 			continue;
9db2f0
-		log_error("pvscan activation for VG %s found different devices.", vgname);
9db2f0
+		log_error_pvscan(cmd, "activation for VG %s found different devices.", vgname);
9db2f0
 		ret = ECMD_FAILED;
9db2f0
 		goto out;
9db2f0
 	}
9db2f0
@@ -1003,7 +1024,7 @@ static int _pvscan_aa_quick(struct cmd_context *cmd, struct pvscan_aa_params *pp
9db2f0
 	log_debug("pvscan autoactivating VG %s.", vgname);
9db2f0
 
9db2f0
 	if (!vgchange_activate(cmd, vg, CHANGE_AAY)) {
9db2f0
-		log_error("%s: autoactivation failed.", vg->name);
9db2f0
+		log_error_pvscan(cmd, "%s: autoactivation failed.", vg->name);
9db2f0
 		pp->activate_errors++;
9db2f0
 	}
9db2f0
 
9db2f0
@@ -1022,7 +1043,7 @@ static int _pvscan_aa(struct cmd_context *cmd, struct pvscan_aa_params *pp,
9db2f0
 	int ret = ECMD_FAILED;
9db2f0
 
9db2f0
 	if (!(handle = init_processing_handle(cmd, NULL))) {
9db2f0
-		log_error("Failed to initialize processing handle.");
9db2f0
+		log_error_pvscan(cmd, "Failed to initialize processing handle.");
9db2f0
 		goto out;
9db2f0
 	}
9db2f0
 
9db2f0
@@ -1037,11 +1058,11 @@ static int _pvscan_aa(struct cmd_context *cmd, struct pvscan_aa_params *pp,
9db2f0
 	 */
9db2f0
 	dm_list_iterate_items_safe(sl, sl2, vgnames) {
9db2f0
 		if (!_online_vg_file_create(cmd, sl->str)) {
9db2f0
-			log_print("pvscan[%d] VG %s skip autoactivation.", getpid(), sl->str);
9db2f0
+			log_print_pvscan(cmd, "VG %s skip autoactivation.", sl->str);
9db2f0
 			str_list_del(vgnames, sl->str);
9db2f0
 			continue;
9db2f0
 		}
9db2f0
-		log_print("pvscan[%d] VG %s run autoactivation.", getpid(), sl->str);
9db2f0
+		log_print_pvscan(cmd, "VG %s run autoactivation.", sl->str);
9db2f0
 	}
9db2f0
 
9db2f0
 	if (dm_list_empty(vgnames)) {
9db2f0
@@ -1189,6 +1210,64 @@ static int _get_args_devs(struct cmd_context *cmd, struct dm_list *pvscan_args,
9db2f0
 	return 1;
9db2f0
 }
9db2f0
 
9db2f0
+static void _set_pv_devices_online(struct cmd_context *cmd, struct volume_group *vg)
9db2f0
+{
9db2f0
+	char path[PATH_MAX];
9db2f0
+	char file_vgname[NAME_LEN];
9db2f0
+	char pvid[ID_LEN+1] = { 0 };
9db2f0
+	struct pv_list *pvl;
9db2f0
+	struct device *dev;
9db2f0
+	int major, minor;
9db2f0
+	dev_t devno;
9db2f0
+
9db2f0
+	dm_list_iterate_items(pvl, &vg->pvs) {
9db2f0
+		memcpy(&pvid, &pvl->pv->id.uuid, ID_LEN);
9db2f0
+
9db2f0
+		if (pvl->pv->status & MISSING_PV) {
9db2f0
+			log_debug("set_pv_devices_online vg %s pv %s missing flag already set",
9db2f0
+				  vg->name, pvid);
9db2f0
+			continue;
9db2f0
+		}
9db2f0
+
9db2f0
+		if (!_online_pvid_file_exists(pvid)) {
9db2f0
+			log_debug("set_pv_devices_online vg %s pv %s no online file",
9db2f0
+				  vg->name, pvid);
9db2f0
+			pvl->pv->status |= MISSING_PV;
9db2f0
+			continue;
9db2f0
+		}
9db2f0
+
9db2f0
+		memset(path, 0, sizeof(path));
9db2f0
+		snprintf(path, sizeof(path), "%s/%s", _pvs_online_dir, pvid);
9db2f0
+
9db2f0
+		major = 0;
9db2f0
+		minor = 0;
9db2f0
+		file_vgname[0] = '\0';
9db2f0
+
9db2f0
+		_online_pvid_file_read(path, &major, &minor, file_vgname);
9db2f0
+
9db2f0
+		if (file_vgname[0] && strcmp(vg->name, file_vgname)) {
9db2f0
+			log_warn("WARNING: VG %s PV %s wrong vgname in online file %s",
9db2f0
+				  vg->name, pvid, file_vgname);
9db2f0
+			pvl->pv->status |= MISSING_PV;
9db2f0
+			continue;
9db2f0
+		}
9db2f0
+
9db2f0
+		devno = MKDEV(major, minor);
9db2f0
+
9db2f0
+		if (!(dev = dev_cache_get_by_devt(cmd, devno, NULL, NULL))) {
9db2f0
+			log_print_pvscan(cmd, "VG %s PV %s no device found for %d:%d",
9db2f0
+					 vg->name, pvid, major, minor);
9db2f0
+			pvl->pv->status |= MISSING_PV;
9db2f0
+			continue;
9db2f0
+		}
9db2f0
+
9db2f0
+		log_debug("set_pv_devices_online vg %s pv %s is online %s",
9db2f0
+			  vg->name, pvid, dev_name(dev));
9db2f0
+
9db2f0
+		pvl->pv->dev = dev;
9db2f0
+	}
9db2f0
+}
9db2f0
+
9db2f0
 static int _online_devs(struct cmd_context *cmd, int do_all, struct dm_list *pvscan_devs,
9db2f0
 			int *pv_count, struct dm_list *complete_vgnames)
9db2f0
 {
9db2f0
@@ -1201,15 +1280,20 @@ static int _online_devs(struct cmd_context *cmd, int do_all, struct dm_list *pvs
9db2f0
 	struct metadata_area *mda1, *mda2;
9db2f0
 	struct volume_group *vg;
9db2f0
 	struct physical_volume *pv;
9db2f0
-	const char *vgname;
9db2f0
-	uint32_t ext_version, ext_flags;
9db2f0
+	const char *vgname = NULL;
9db2f0
 	uint64_t devsize;
9db2f0
+	uint32_t ext_version, ext_flags;
9db2f0
+	int do_cache = arg_is_set(cmd, cache_long_ARG);
9db2f0
 	int do_activate = arg_is_set(cmd, activate_ARG);
9db2f0
-	int do_full_check;
9db2f0
+	int do_list_lvs = arg_is_set(cmd, listlvs_ARG);
9db2f0
+	int do_list_vg = arg_is_set(cmd, listvg_ARG);
9db2f0
+	int do_check_complete = arg_is_set(cmd, checkcomplete_ARG);
9db2f0
+	int do_vgonline = arg_is_set(cmd, vgonline_ARG);
9db2f0
 	int pvs_online;
9db2f0
 	int pvs_offline;
9db2f0
 	int pvs_unknown;
9db2f0
 	int vg_complete;
9db2f0
+	int do_full_check;
9db2f0
 	int ret = 1;
9db2f0
 
9db2f0
 	dm_list_iterate_items_safe(devl, devl2, pvscan_devs) {
9db2f0
@@ -1219,14 +1303,14 @@ static int _online_devs(struct cmd_context *cmd, int do_all, struct dm_list *pvs
9db2f0
 
9db2f0
 		if (!(info = lvmcache_info_from_pvid(dev->pvid, dev, 0))) {
9db2f0
 			if (!do_all)
9db2f0
-				log_print("pvscan[%d] ignore %s with no lvm info.", getpid(), dev_name(dev));
9db2f0
+				log_print_pvscan(cmd, "ignore %s with no lvm info.", dev_name(dev));
9db2f0
 			continue;
9db2f0
 		}
9db2f0
 
9db2f0
 		ext_version = lvmcache_ext_version(info);
9db2f0
 		ext_flags = lvmcache_ext_flags(info);
9db2f0
 		if ((ext_version >= 2) && !(ext_flags & PV_EXT_USED)) {
9db2f0
-			log_print("pvscan[%d] PV %s not used.", getpid(), dev_name(dev));
9db2f0
+			log_print_pvscan(cmd, "PV %s not used.", dev_name(dev));
9db2f0
 			(*pv_count)++;
9db2f0
 			continue;
9db2f0
 		}
9db2f0
@@ -1245,7 +1329,7 @@ static int _online_devs(struct cmd_context *cmd, int do_all, struct dm_list *pvs
9db2f0
 			vg = mda2->ops->vg_read(cmd, fid, "", mda2, NULL, NULL);
9db2f0
 
9db2f0
 		if (!vg) {
9db2f0
-			log_print("pvscan[%d] PV %s has no VG metadata.", getpid(), dev_name(dev));
9db2f0
+			log_print_pvscan(cmd, "PV %s has no VG metadata.", dev_name(dev));
9db2f0
 			if (fid)
9db2f0
 				fmt->ops->destroy_instance(fid);
9db2f0
 			goto online;
9db2f0
@@ -1254,7 +1338,7 @@ static int _online_devs(struct cmd_context *cmd, int do_all, struct dm_list *pvs
9db2f0
 		set_pv_devices(fid, vg);
9db2f0
 
9db2f0
 		if (!(pv = find_pv(vg, dev))) {
9db2f0
-			log_print("pvscan[%d] PV %s not found in VG %s.", getpid(), dev_name(dev), vg->name);
9db2f0
+			log_print_pvscan(cmd, "PV %s not found in VG %s.", dev_name(dev), vg->name);
9db2f0
 			release_vg(vg);
9db2f0
 			continue;
9db2f0
 		}
9db2f0
@@ -1271,14 +1355,15 @@ static int _online_devs(struct cmd_context *cmd, int do_all, struct dm_list *pvs
9db2f0
 			if (pv->device_hint && !strncmp(pv->device_hint, "/dev/md", 7))
9db2f0
 				do_full_check = 1;
9db2f0
 		}
9db2f0
+
9db2f0
 		if (do_full_check && dev_is_md_component(cmd, dev, NULL, 1)) {
9db2f0
-			log_print("pvscan[%d] ignore md component %s.", getpid(), dev_name(dev));
9db2f0
+			log_print_pvscan(cmd, "ignore md component %s.", dev_name(dev));
9db2f0
 			release_vg(vg);
9db2f0
 			continue;
9db2f0
 		}
9db2f0
 
9db2f0
 		if (vg_is_shared(vg)) {
9db2f0
-			log_print("pvscan[%d] PV %s ignore shared VG.", getpid(), dev_name(dev));
9db2f0
+			log_print_pvscan(cmd, "PV %s ignore shared VG.", dev_name(dev));
9db2f0
 			release_vg(vg);
9db2f0
 			continue;
9db2f0
 		}
9db2f0
@@ -1288,7 +1373,13 @@ static int _online_devs(struct cmd_context *cmd, int do_all, struct dm_list *pvs
9db2f0
 		    vg_is_foreign(vg)) {
9db2f0
 			log_verbose("Ignore PV %s with VG system id %s with our system id %s",
9db2f0
 				    dev_name(dev), vg->system_id, cmd->system_id);
9db2f0
-			log_print("pvscan[%d] PV %s ignore foreign VG.", getpid(), dev_name(dev));
9db2f0
+			log_print_pvscan(cmd, "PV %s ignore foreign VG.", dev_name(dev));
9db2f0
+			release_vg(vg);
9db2f0
+			continue;
9db2f0
+		}
9db2f0
+
9db2f0
+		if (vg_is_exported(vg)) {
9db2f0
+			log_print_pvscan(cmd, "PV %s ignore exported VG.", dev_name(dev));
9db2f0
 			release_vg(vg);
9db2f0
 			continue;
9db2f0
 		}
9db2f0
@@ -1307,19 +1398,20 @@ static int _online_devs(struct cmd_context *cmd, int do_all, struct dm_list *pvs
9db2f0
 
9db2f0
 		/*
9db2f0
 		 * Create file named for pvid to record this PV is online.
9db2f0
+		 * The command creates/checks online files only when --cache is used.
9db2f0
 		 */
9db2f0
-		if (!_online_pvid_file_create(dev, vg ? vg->name : NULL)) {
9db2f0
-			log_error("pvscan[%d] PV %s failed to create online file.", getpid(), dev_name(dev));
9db2f0
+		if (do_cache && !_online_pvid_file_create(cmd, dev, vg ? vg->name : NULL)) {
9db2f0
+			log_error_pvscan(cmd, "PV %s failed to create online file.", dev_name(dev));
9db2f0
 			release_vg(vg);
9db2f0
 			ret = 0;
9db2f0
 			continue;
9db2f0
 		}
9db2f0
 
9db2f0
 		/*
9db2f0
-		 * When not activating we don't need to know about vg completeness.
9db2f0
+		 * A plain pvscan --cache <dev> just creates the online file.
9db2f0
 		 */
9db2f0
-		if (!do_activate) {
9db2f0
-			log_print("pvscan[%d] PV %s online.", getpid(), dev_name(dev));
9db2f0
+		if (!do_activate && !do_list_lvs && !do_list_vg) {
9db2f0
+			log_print_pvscan(cmd, "PV %s online.", dev_name(dev));
9db2f0
 			release_vg(vg);
9db2f0
 			continue;
9db2f0
 		}
9db2f0
@@ -1327,58 +1419,159 @@ static int _online_devs(struct cmd_context *cmd, int do_all, struct dm_list *pvs
9db2f0
 		/*
9db2f0
 		 * Check if all the PVs for this VG are online.  If the arrival
9db2f0
 		 * of this dev completes the VG, then save the vgname in
9db2f0
-		 * complete_vgnames so it will be activated.
9db2f0
+		 * complete_vgnames (activation phase will want to know which
9db2f0
+		 * VGs to activate.)
9db2f0
 		 */
9db2f0
-		pvs_online = 0;
9db2f0
-		pvs_offline = 0;
9db2f0
-		pvs_unknown = 0;
9db2f0
-		vg_complete = 0;
9db2f0
-
9db2f0
-		if (vg) {
9db2f0
-			/*
9db2f0
-			 * Use the VG metadata from this PV for a list of all
9db2f0
-			 * PVIDs.  Write a lookup file of PVIDs in case another
9db2f0
-			 * pvscan needs it.  After writing lookup file, recheck
9db2f0
-			 * pvid files to resolve a possible race with another
9db2f0
-			 * pvscan reading the lookup file that missed it.
9db2f0
-			 */
9db2f0
-			log_debug("checking all pvid files from vg %s", vg->name);
9db2f0
-			_count_pvid_files(vg, &pvs_online, &pvs_offline);
9db2f0
+		if (do_activate || do_check_complete) {
9db2f0
+			pvs_online = 0;
9db2f0
+			pvs_offline = 0;
9db2f0
+			pvs_unknown = 0;
9db2f0
+			vg_complete = 0;
9db2f0
 
9db2f0
-			if (pvs_offline && _write_lookup_file(cmd, vg)) {
9db2f0
-				log_debug("rechecking all pvid files from vg %s", vg->name);
9db2f0
+			if (vg) {
9db2f0
+				/*
9db2f0
+				 * Use the VG metadata from this PV for a list of all
9db2f0
+				 * PVIDs.  Write a lookup file of PVIDs in case another
9db2f0
+				 * pvscan needs it.  After writing lookup file, recheck
9db2f0
+				 * pvid files to resolve a possible race with another
9db2f0
+				 * pvscan reading the lookup file that missed it.
9db2f0
+				 */
9db2f0
+				log_debug("checking all pvid files from vg %s", vg->name);
9db2f0
 				_count_pvid_files(vg, &pvs_online, &pvs_offline);
9db2f0
-				if (!pvs_offline)
9db2f0
-					log_print("pvscan[%d] VG %s complete after recheck.", getpid(), vg->name);
9db2f0
+	
9db2f0
+				if (pvs_offline && _write_lookup_file(cmd, vg)) {
9db2f0
+					log_debug("rechecking all pvid files from vg %s", vg->name);
9db2f0
+					_count_pvid_files(vg, &pvs_online, &pvs_offline);
9db2f0
+					if (!pvs_offline)
9db2f0
+						log_print_pvscan(cmd, "VG %s complete after recheck.", vg->name);
9db2f0
+				}
9db2f0
+	
9db2f0
+				vgname = vg->name;
9db2f0
+			} else {
9db2f0
+				/*
9db2f0
+				 * No VG metadata on this PV, so try to use a lookup
9db2f0
+				 * file written by a prior pvscan for a list of all
9db2f0
+				 * PVIDs.  A lookup file may not exist for this PV if
9db2f0
+				 * it's the first to appear from the VG.
9db2f0
+				 */
9db2f0
+				log_debug("checking all pvid files from lookup file");
9db2f0
+				if (!_count_pvid_files_from_lookup_file(cmd, dev, &pvs_online, &pvs_offline, &vgname))
9db2f0
+					pvs_unknown = 1;
9db2f0
+			}
9db2f0
+	
9db2f0
+			if (pvs_unknown) {
9db2f0
+				log_print_pvscan(cmd, "PV %s online, VG unknown.", dev_name(dev));
9db2f0
+				vg_complete = 0;
9db2f0
+	
9db2f0
+			} else if (pvs_offline) {
9db2f0
+				log_print_pvscan(cmd, "PV %s online, VG %s incomplete (need %d).",
9db2f0
+						 dev_name(dev), vgname, pvs_offline);
9db2f0
+				vg_complete = 0;
9db2f0
+	
9db2f0
+			} else {
9db2f0
+				log_print_pvscan(cmd, "PV %s online, VG %s is complete.", dev_name(dev), vgname);
9db2f0
+				if (!str_list_add(cmd->mem, complete_vgnames, dm_pool_strdup(cmd->mem, vgname)))
9db2f0
+					stack;
9db2f0
+				vg_complete = 1;
9db2f0
 			}
9db2f0
+		}
9db2f0
 
9db2f0
+		if (!vgname && vg)
9db2f0
 			vgname = vg->name;
9db2f0
-		} else {
9db2f0
+
9db2f0
+		if (do_list_vg || do_list_lvs) {
9db2f0
+			if (!vgname) {
9db2f0
+				log_print("VG unknown");
9db2f0
+			} else if (!do_check_complete) {
9db2f0
+				log_print("VG %s", vgname);
9db2f0
+			} else if (vg_complete) {
9db2f0
+				if (do_vgonline && !_online_vg_file_create(cmd, vgname)) {
9db2f0
+					log_print("VG %s finished", vgname);
9db2f0
+				} else {
9db2f0
+					/*
9db2f0
+					 * A udev rule imports KEY=val from a program's stdout.
9db2f0
+					 * Other output causes udev to ignore everything.
9db2f0
+					 * Run pvscan from udev rule using --udevoutput to
9db2f0
+					 * enable this printf, and suppress all log output
9db2f0
+					 */
9db2f0
+					if (arg_is_set(cmd, udevoutput_ARG))
9db2f0
+						printf("LVM_VG_NAME_COMPLETE='%s'\n", vgname);
9db2f0
+					else
9db2f0
+						log_print("VG %s complete", vgname);
9db2f0
+				}
9db2f0
+			} else {
9db2f0
+				if (arg_is_set(cmd, udevoutput_ARG))
9db2f0
+					printf("LVM_VG_NAME_INCOMPLETE='%s'\n", vgname);
9db2f0
+				else
9db2f0
+					log_print("VG %s incomplete", vgname);
9db2f0
+			}
9db2f0
+
9db2f0
 			/*
9db2f0
-			 * No VG metadata on this PV, so try to use a lookup
9db2f0
-			 * file written by a prior pvscan for a list of all
9db2f0
-			 * PVIDs.  A lookup file may not exist for this PV if
9db2f0
-			 * it's the first to appear from the VG.
9db2f0
+			 * When the VG is complete|finished, we could print
9db2f0
+			 * a list of devices in the VG, by reading the pvid files
9db2f0
+			 * that were counted, which provides major:minor of each
9db2f0
+			 * device and using that to get the struct dev and dev_name.
9db2f0
+			 * The user could pass this list of devices to --devices
9db2f0
+			 * to optimize a subsequent command (activation) on the VG.
9db2f0
+			 * Just call set_pv_devices_online (if not done othewise)
9db2f0
+			 * since that finds the devs.
9db2f0
 			 */
9db2f0
-			log_debug("checking all pvid files from lookup file");
9db2f0
-			if (!_count_pvid_files_from_lookup_file(cmd, dev, &pvs_online, &pvs_offline, &vgname))
9db2f0
-				pvs_unknown = 1;
9db2f0
 		}
9db2f0
 
9db2f0
-		if (pvs_unknown) {
9db2f0
-			log_print("pvscan[%d] PV %s online, VG unknown.",
9db2f0
-				  getpid(), dev_name(dev));
9db2f0
-		} else if (pvs_offline) {
9db2f0
-			log_print("pvscan[%d] PV %s online, VG %s incomplete (need %d).",
9db2f0
-				  getpid(), dev_name(dev), vgname, pvs_offline);
9db2f0
-		} else {
9db2f0
-			log_print("pvscan[%d] PV %s online, VG %s is complete.", getpid(), dev_name(dev), vgname);
9db2f0
-			if (!str_list_add(cmd->mem, complete_vgnames, dm_pool_strdup(cmd->mem, vgname)))
9db2f0
-				stack;
9db2f0
-			vg_complete = 1;
9db2f0
+		if (do_list_lvs && !vg) {
9db2f0
+			/* require all PVs used for booting have metadata */
9db2f0
+			log_print_pvscan(cmd, "Cannot list LVs from device without metadata.");
9db2f0
 		}
9db2f0
 
9db2f0
-		if (!saved_vg && vg && vg_complete && !do_all && (dm_list_size(pvscan_devs) == 1))
9db2f0
+		if (do_list_lvs && vg) {
9db2f0
+			struct dm_list lvs_list;
9db2f0
+			struct lv_list *lvl;
9db2f0
+
9db2f0
+			dm_list_init(&lvs_list);
9db2f0
+
9db2f0
+			/*
9db2f0
+			 * For each vg->pvs entry, get the dev based on the online file
9db2f0
+			 * for the pvid and set pv->dev or pv->status MISSING_PV.
9db2f0
+			 */
9db2f0
+			_set_pv_devices_online(cmd, vg);
9db2f0
+
9db2f0
+			/*
9db2f0
+			 * lvs_list are LVs that use dev.
9db2f0
+			 */
9db2f0
+			if (!get_visible_lvs_using_pv(cmd, vg, dev, &lvs_list))
9db2f0
+				log_print_pvscan(cmd, "Failed to find LVs using %s.", dev_name(dev));
9db2f0
+
9db2f0
+			if (!do_check_complete) {
9db2f0
+				dm_list_iterate_items(lvl, &lvs_list)
9db2f0
+					log_print("LV %s", display_lvname(lvl->lv));
9db2f0
+			} else if (vg_complete) {
9db2f0
+				/*
9db2f0
+				 * A shortcut; the vg complete implies all lvs are complete.
9db2f0
+				 */
9db2f0
+				dm_list_iterate_items(lvl, &lvs_list)
9db2f0
+					log_print("LV %s complete", display_lvname(lvl->lv));
9db2f0
+			} else {
9db2f0
+				/*
9db2f0
+				 * For each LV in VG, check if all devs are present.
9db2f0
+				 * Sets the PARTIAL flag on LVs that are not complete.
9db2f0
+				 */
9db2f0
+				if (!vg_mark_partial_lvs(vg, 1))
9db2f0
+					log_print_pvscan(cmd, "Failed to check partial lvs.");
9db2f0
+
9db2f0
+				dm_list_iterate_items(lvl, &lvs_list) {
9db2f0
+					if (!lv_is_partial(lvl->lv))
9db2f0
+						log_print("LV %s complete", display_lvname(lvl->lv));
9db2f0
+					else
9db2f0
+						log_print("LV %s incomplete", display_lvname(lvl->lv));
9db2f0
+				}
9db2f0
+			}
9db2f0
+		}
9db2f0
+
9db2f0
+		/*
9db2f0
+		 * When "pvscan --cache -aay <dev>" completes the vg, save the
9db2f0
+		 * struct vg to use for quick activation function.
9db2f0
+		 */
9db2f0
+		if (do_activate && !saved_vg && vg && vg_complete && !do_all && (dm_list_size(pvscan_devs) == 1))
9db2f0
 			saved_vg = vg;
9db2f0
 		else
9db2f0
 			release_vg(vg);
9db2f0
@@ -1472,7 +1665,7 @@ static int _pvscan_cache_args(struct cmd_context *cmd, int argc, char **argv,
9db2f0
 	 * Specific devs must be matched later with device_ids_match_dev().
9db2f0
 	 */
9db2f0
 	if (!setup_devices_no_file_match(cmd)) {
9db2f0
-		log_error("Failed to set up devices.");
9db2f0
+		log_error_pvscan(cmd, "Failed to set up devices.");
9db2f0
 		return 0;
9db2f0
 	}
9db2f0
 
9db2f0
@@ -1551,8 +1744,8 @@ static int _pvscan_cache_args(struct cmd_context *cmd, int argc, char **argv,
9db2f0
 
9db2f0
 	dm_list_iterate_items_safe(devl, devl2, &pvscan_devs) {
9db2f0
 		if (!cmd->filter->passes_filter(cmd, cmd->filter, devl->dev, NULL)) {
9db2f0
-			log_print("pvscan[%d] %s excluded by filters: %s.", getpid(),
9db2f0
-				  dev_name(devl->dev), dev_filtered_reason(devl->dev));
9db2f0
+			log_print_pvscan(cmd, "%s excluded by filters: %s.",
9db2f0
+					 dev_name(devl->dev), dev_filtered_reason(devl->dev));
9db2f0
 			dm_list_del(&devl->list);
9db2f0
 		}
9db2f0
 	}
9db2f0
@@ -1588,7 +1781,7 @@ static int _pvscan_cache_args(struct cmd_context *cmd, int argc, char **argv,
9db2f0
 
9db2f0
 		if (!has_pvid) {
9db2f0
 			/* Not an lvm device */
9db2f0
-			log_print("pvscan[%d] %s not an lvm device.", getpid(), dev_name(devl->dev));
9db2f0
+			log_print_pvscan(cmd, "%s not an lvm device.", dev_name(devl->dev));
9db2f0
 			dm_list_del(&devl->list);
9db2f0
 			continue;
9db2f0
 		}
9db2f0
@@ -1599,8 +1792,8 @@ static int _pvscan_cache_args(struct cmd_context *cmd, int argc, char **argv,
9db2f0
 		 */
9db2f0
 		if (relax_deviceid_filter) {
9db2f0
 			if (!get_du_for_pvid(cmd, devl->dev->pvid)) {
9db2f0
-				log_print("pvscan[%d] %s excluded by devices file (checking PVID).",
9db2f0
-					  getpid(), dev_name(devl->dev));
9db2f0
+				log_print_pvscan(cmd, "%s excluded by devices file (checking PVID).",
9db2f0
+					         dev_name(devl->dev));
9db2f0
 				dm_list_del(&devl->list);
9db2f0
 				continue;
9db2f0
 			}
9db2f0
@@ -1608,8 +1801,8 @@ static int _pvscan_cache_args(struct cmd_context *cmd, int argc, char **argv,
9db2f0
 
9db2f0
 		/* Applies all filters, including those that need data from dev. */
9db2f0
 		if (!cmd->filter->passes_filter(cmd, cmd->filter, devl->dev, NULL)) {
9db2f0
-			log_print("pvscan[%d] %s excluded by filters: %s.", getpid(),
9db2f0
-				  dev_name(devl->dev), dev_filtered_reason(devl->dev));
9db2f0
+			log_print_pvscan(cmd, "%s excluded by filters: %s.",
9db2f0
+					 dev_name(devl->dev), dev_filtered_reason(devl->dev));
9db2f0
 			dm_list_del(&devl->list);
9db2f0
 		}
9db2f0
 	}
9db2f0
@@ -1673,6 +1866,37 @@ int pvscan_cache_cmd(struct cmd_context *cmd, int argc, char **argv)
9db2f0
 		return ECMD_PROCESSED;
9db2f0
 	}
9db2f0
 
9db2f0
+	/*
9db2f0
+	 * lvm udev rules call:
9db2f0
+	 *   pvscan --cache --listvg|--listlvs --checkcomplete PV
9db2f0
+	 * when PVs appear, even if event_activation=0 in lvm.conf.
9db2f0
+	 *
9db2f0
+	 * The udev rules will do autoactivation if they see complete
9db2f0
+	 * VGs/LVs reported from the pvscan.
9db2f0
+	 *
9db2f0
+	 * When event_activation=0 we do not want to do autoactivation
9db2f0
+	 * from udev events, so we need the pvscan to not report any
9db2f0
+	 * complete VGs/LVs when event_activation=0 so that the udev
9db2f0
+	 * rules do not attempt to autoactivate.
9db2f0
+	 */
9db2f0
+
9db2f0
+	if (arg_is_set(cmd, checkcomplete_ARG) && !event_activation) {
9db2f0
+		if (arg_is_set(cmd, udevoutput_ARG))
9db2f0
+			printf("LVM_EVENT_ACTIVATION=0\n");
9db2f0
+		else
9db2f0
+			log_print_pvscan(cmd, "Ignoring pvscan with --checkcomplete because event_activation is disabled.");
9db2f0
+		return ECMD_PROCESSED;
9db2f0
+	}
9db2f0
+
9db2f0
+	/*
9db2f0
+	 * If obtain_device_list_from_udev was set to 1, force it to 0.
9db2f0
+	 * Don't ask udev for info since pvscan is running from udev.
9db2f0
+	 * If a pvscan attempts to get dev info from udev, udev can
9db2f0
+	 * repeatedly return errors about the dev not being initialized
9db2f0
+	 * which will stall the pvscan.
9db2f0
+	 */
9db2f0
+	init_obtain_device_list_from_udev(0);
9db2f0
+
9db2f0
 	if (arg_is_set(cmd, major_ARG) + arg_is_set(cmd, minor_ARG))
9db2f0
 		devno_args = 1;
9db2f0
 
9db2f0
@@ -1683,13 +1907,13 @@ int pvscan_cache_cmd(struct cmd_context *cmd, int argc, char **argv)
9db2f0
 
9db2f0
 	do_all = !argc && !devno_args;
9db2f0
 
9db2f0
-	_online_dir_setup();
9db2f0
+	_online_dir_setup(cmd);
9db2f0
 
9db2f0
 	if (do_all) {
9db2f0
 		if (!_pvscan_cache_all(cmd, argc, argv, &complete_vgnames))
9db2f0
 			return ECMD_FAILED;
9db2f0
 	} else {
9db2f0
-		if (!event_activation) {
9db2f0
+		if (!arg_is_set(cmd, checkcomplete_ARG) && !event_activation) {
9db2f0
 			/* Avoid doing anything for device removal: pvscan --cache <devno> */
9db2f0
 			log_verbose("Ignoring pvscan --cache because event_activation is disabled.");
9db2f0
 			return ECMD_PROCESSED;
9db2f0
diff --git a/tools/toollib.c b/tools/toollib.c
9db2f0
index 3385510..6ef3895 100644
9db2f0
--- a/tools/toollib.c
9db2f0
+++ b/tools/toollib.c
9db2f0
@@ -811,6 +811,24 @@ int lv_change_activate(struct cmd_context *cmd, struct logical_volume *lv,
9db2f0
 		lv_clear_integrity_recalculate_metadata(lv);
9db2f0
 	}
9db2f0
 
9db2f0
+	/*
9db2f0
+	 * When LVs are deactivated, then autoactivation of the VG is
9db2f0
+	 * "re-armed" by removing the vg online file.  So, after deactivation
9db2f0
+	 * of LVs, if PVs are disconnected and reconnected again, event
9db2f0
+	 * activation will trigger autoactivation again.  This secondary
9db2f0
+	 * autoactivation is somewhat different from, and not as important as
9db2f0
+	 * the initial autoactivation during system startup.  The secondary
9db2f0
+	 * autoactivation will happen to a VG on a running system and may be
9db2f0
+	 * mixing with user commands, so the end result is unpredictable.
9db2f0
+	 *
9db2f0
+	 * It's possible that we might want a config setting for usersto  
9db2f0
+	 * disable secondary autoactivations.  Once a system is up, the
9db2f0
+	 * user may want to take charge of activation changes to the VG
9db2f0
+	 * and not have the system autoactivation interfere.
9db2f0
+	 */
9db2f0
+	if (!is_change_activating(activate) && find_config_tree_bool(cmd, global_event_activation_CFG, NULL))
9db2f0
+		online_vg_file_remove(lv->vg->name);
9db2f0
+
9db2f0
 	set_lv_notify(lv->vg->cmd);
9db2f0
 
9db2f0
 	return r;
9db2f0
diff --git a/tools/tools.h b/tools/tools.h
9db2f0
index 708a78d..bc98fcb 100644
9db2f0
--- a/tools/tools.h
9db2f0
+++ b/tools/tools.h
9db2f0
@@ -295,4 +295,6 @@ int lvconvert_cachevol_attach_single(struct cmd_context *cmd,
9db2f0
                                      struct logical_volume *lv,
9db2f0
                                      struct processing_handle *handle);
9db2f0
 
9db2f0
+void online_vg_file_remove(const char *vgname);
9db2f0
+
9db2f0
 #endif
9db2f0
-- 
9db2f0
1.8.3.1
9db2f0